Skip to content

ajustes eslint y diseño#119

Merged
henri318 merged 3 commits into
mainfrom
feat/module_payments_pr5
Jul 6, 2026
Merged

ajustes eslint y diseño#119
henri318 merged 3 commits into
mainfrom
feat/module_payments_pr5

Conversation

@henri318

@henri318 henri318 commented Jul 6, 2026

Copy link
Copy Markdown
Owner

ajustes eslint y diseño

Summary by CodeRabbit

  • New Features

    • Checkout, pedidos y menú de usuario ahora están mejor localizados y muestran textos según el idioma.
    • La vista de pedidos incluye listado más completo, búsqueda, filtros, paginación y detalle de cada pedido con sus líneas.
    • Se añadió la acción de reintentar pagos fallidos desde las listas y el detalle.
  • Bug Fixes

    • Se mejoró la información mostrada en pedidos con nombres, imágenes y precios por línea.
    • Se ajustaron estilos y mensajes para una experiencia más consistente en checkout y carrito.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@henri318, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f0be8724-e57d-40f3-b936-78127d793c83

📥 Commits

Reviewing files that changed from the base of the PR and between a2e58b5 and e7fb579.

📒 Files selected for processing (22)
  • app/[locale]/cart/page.tsx
  • app/[locale]/orders/[orderId]/page.tsx
  • app/[locale]/orders/page.tsx
  • app/[locale]/seller/orders/page.tsx
  • app/[locale]/seller/products/[id]/edit/page.tsx
  • app/[locale]/seller/products/new/page.tsx
  • app/[locale]/seller/products/product-form.tsx
  • app/api/orders/[orderId]/status/route.ts
  • modules/cart/presentation/components/add-to-cart-button.tsx
  • modules/cart/presentation/components/cart-view.tsx
  • modules/orders/infrastructure/prisma-order-repository.ts
  • modules/orders/presentation/schemas/order-schemas.ts
  • modules/products/presentation/components/product-customization-config-editor.tsx
  • shared/i18n/locales/cat.json
  • shared/i18n/locales/es.json
  • shared/i18n/normalize-locale.ts
  • shared/ui/tag-list.tsx
  • tests/unit/app/[locale]/cart/cart-view-customization.test.tsx
  • tests/unit/app/[locale]/checkout/page.test.tsx
  • tests/unit/app/[locale]/seller/products/product-form.test.tsx
  • tests/unit/app/cart/cart-view-guest.test.tsx
  • tests/unit/app/cart/cart-view.test.tsx
📝 Walkthrough

Walkthrough

Se añade internacionalización completa a checkout, carrito, página de producto y menú de usuario mediante diccionarios es/cat. Se incorporan unitPrice y productImageUrl a los line items de pedidos (schema, migración, repositorio, casos de uso), y se rediseñan las páginas de listado/detalle de pedidos (cliente/vendedor) usando DataTable, Pagination, Card y StatusBadge, con búsqueda por q. Se actualizan estilos CSS a variables de tema, se añade eslint-plugin-i18next y se actualizan tests correspondientes.

Changes

Pedidos: unitPrice, imagen y páginas rediseñadas

Layer / File(s) Summary
Contrato de dominio y schema
modules/orders/domain/entities/order-line-item.ts, modules/orders/domain/order-repository.ts, modules/orders/presentation/schemas/order-schemas.ts, prisma/schema.prisma, prisma/migrations/.../migration.sql
Se añaden unitPrice, productImageUrl y filtro q, junto con la migración e índice correspondientes.
Repositorio y casos de uso
modules/orders/infrastructure/prisma-order-repository.ts, modules/orders/application/*.ts
findPaginated/findById aceptan locale, cargan traducciones/imágenes y mapean unitPrice/productName/productImageUrl; los casos de uso propagan locale y unitPrice.
Detalle de pedido
app/[locale]/orders/[orderId]/page.tsx, page.module.css
Se muestra el pedido con StatusBadge, Card, imagen/nombre/personalización/cantidad y total por línea, con textos localizados.
Listado de pedidos del cliente
app/[locale]/orders/page.tsx, page.module.css
Se reemplaza la tabla manual por DataTable, SearchForm, Pagination y Card, con búsqueda por q.
Listado de pedidos del vendedor
app/[locale]/seller/orders/page.tsx, page.module.css, app/api/orders/[orderId]/status/route.ts
Se rediseña con DataTable/filtros/Pagination, y el endpoint de status redirige según referer.
Estilos y tests de soporte
shared/ui/status-badge.module.css, tests/**
Se añaden estilos por estado y se actualizan fixtures de tests con unitPrice.

Internacionalización de checkout, carrito y páginas de producto

Layer / File(s) Summary
Diccionarios
shared/i18n/locales/es.json, cat.json
Se añaden nuevas claves de traducción para checkout, pedidos y menú de usuario.
Checkout
app/[locale]/checkout/page.tsx, page.module.css
Textos traducidos y estilos migrados a variables de tema.
Carrito
modules/cart/presentation/components/*.tsx, *.module.css, app/[locale]/layout.tsx
CartPopupLabels añade close; CheckoutConfirmButton y CartView usan dict/useDictionary.
Producto y componentes compartidos
app/[locale]/products/[id]/page.tsx, shared/ui/tag-list.tsx, shared/ui/price-field.tsx, modules/products/presentation/components/product-customization-config-editor.tsx
Textos localizados y currencySymbol configurable.
Menú de usuario
shared/layout/user-menu-dropdown.tsx
Se añade enlace a pedidos del diseñador.
Tests
tests/unit/**
Se actualizan aserciones a los nuevos textos localizados.

Configuración ESLint, dependencias y ajuste menor

Layer / File(s) Summary
ESLint y dependencias
eslint.config.mjs, package.json
Se añade eslint-plugin-i18next con regla no-literal-string y allowScripts.
Ajuste de accesibilidad
app/[locale]/products/[id]/mockup-canvas-control.tsx
Se construyen caracteres mediante String.fromCharCode.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • henri318/728-store#54: Añade la suite de tests de integración de PrismaOrderRepository que este PR actualiza con el nuevo campo unitPrice.
  • henri318/728-store#107: Introduce los componentes UI compartidos (Card, DataTable, Pagination, StatusBadge) que este PR adopta en las páginas de pedidos.
  • henri318/728-store#105: Modifica el mismo buildLineItems en handle-cart-checked-out.ts para enriquecer los line items de pedidos.

Suggested labels: type:feature, type:chore

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive El título menciona ESLint y diseño, pero es demasiado genérico y no resume claramente los cambios principales. Usa un título más específico, por ejemplo: "Añade i18n, ajustes de pedidos y mejoras de estilos".
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/module_payments_pr5

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
shared/ui/tag-list.tsx (1)

94-101: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

aria-label del botón eliminar no describe la acción.

aria-label={tag} solo anuncia el valor de la etiqueta, sin indicar que el botón la elimina. Para lectores de pantalla sería más claro combinar la acción con el valor, p. ej. usando dict.common.removeFromCart.

♿ Propuesta de fix
-                aria-label={tag}
+                aria-label={`${dict.common.removeFromCart}: ${tag}`}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shared/ui/tag-list.tsx` around lines 94 - 101, The remove button in TagList
does not announce its action clearly because aria-label only uses the tag value.
Update the button in tag-list.tsx to use an action-oriented accessible label in
the TagList render logic, combining the remove action with the tag name (similar
to dict.common.removeFromCart) so screen readers hear that the button removes
that specific tag.
modules/orders/infrastructure/prisma-order-repository.ts (1)

163-185: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

productImageUrl nunca se persiste en saveOrderLineItems.

El createMany solo añade unitPrice; productImageUrl no forma parte del payload pese a que el modelo Prisma lo define (productImageUrl String?) y OrderLineItemEntity lo documenta como "Product image URL (snapshot at checkout time)". Esto implica que la columna siempre quedará en NULL, y tanto findPaginated como findById acabarán resolviendo siempre item.product?.images?.[0]?.url (imagen actual del producto) en el fallback ?? item.productImageUrl, nunca la imagen histórica del momento de la compra. Si el vendedor cambia la foto del producto, el historial de pedidos mostrará silenciosamente una imagen distinta a la que el cliente compró.

Confirmé además que ni create-order-use-case.ts (línea 93) ni handle-cart-checked-out.ts (línea 161) rellenan productImageUrl al construir el OrderLineItemEntity, por lo que el campo está completamente desconectado de extremo a extremo.

La descripción de esta capa indica que se mapea "productName/productImageUrl/unitPrice tanto en findPaginated como en findById y saveOrderLineItems", pero el código de saveOrderLineItems solo cubre unitPrice.

🛠️ Fix propuesto (mínimo, requiere además poblar el campo en los casos de uso)
     await tx.orderLineItem.createMany({
       data: lineItems.map((item) => ({
         id: item.id,
         orderId: orderId,
         productId: item.productId,
         quantity: item.quantity,
         unitPrice: item.unitPrice,
+        productImageUrl: item.productImageUrl ?? null,
         customizationIdList: item.customizationIdList,
         customizationSnapshot: (item.customizationSnapshot ??
           Prisma.JsonNull) as unknown as Prisma.InputJsonValue,
       })),
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/orders/infrastructure/prisma-order-repository.ts` around lines 163 -
185, `saveOrderLineItems` no está persistiendo `productImageUrl`, así que el
snapshot de imagen queda perdido y los lectores posteriores caen en el fallback
de la imagen actual del producto. Actualiza el payload del
`tx.orderLineItem.createMany` para incluir `productImageUrl` desde
`OrderLineItemEntity`, y verifica que los puntos que construyen esos entities,
como `create-order-use-case` y `handle-cart-checked-out`, asignen ese campo al
momento del checkout.
🧹 Nitpick comments (9)
app/[locale]/products/[id]/mockup-canvas-control.tsx (1)

292-296: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

String.fromCharCode innecesario: no evita ningún lint real.

La regla i18next/no-literal-string está configurada en modo jsx-text-only, que "solo prohíbe escribir texto plano dentro del markup JSX" y no se aplica a template literals dentro de un useMemo. Este cambio no aporta beneficio de linting y solo reduce la legibilidad.

♻️ Revertir a literales simples
   const positionReadout = useMemo(
-    () =>
-      `${Math.round(position.x * 100)}${String.fromCharCode(37)} ${String.fromCharCode(47)} ${Math.round(position.y * 100)}${String.fromCharCode(37)}`,
+    () =>
+      `${Math.round(position.x * 100)}% / ${Math.round(position.y * 100)}%`,
     [position.x, position.y],
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/products/[id]/mockup-canvas-control.tsx around lines 292 - 296,
The String.fromCharCode change in useMemo for positionReadout is unnecessary
because the i18next/no-literal-string rule only targets JSX text, not this
template literal. Revert the expression in mockup-canvas-control.tsx to simple
literal punctuation while keeping the same positionReadout calculation, and use
the useMemo block as the place to restore readability.
package.json (1)

118-123: 🔒 Security & Privacy | 🔵 Trivial

Revisar el alcance de allowScripts.

Habilitar la ejecución de scripts de instalación amplía la superficie de confianza de la cadena de suministro. Dado que son paquetes nativos conocidos (prisma, bcrypt, esbuild, sharp), es razonable, pero conviene fijar versiones exactas y revisar periódicamente advisories de estas dependencias antes de actualizar.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` around lines 118 - 123, Review the allowScripts whitelist in
package.json and keep it narrowly scoped to the exact native packages and
versions already intended here (`@prisma/engines`@7.8.0, prisma@7.8.0,
bcrypt@6.0.0, esbuild@0.28.1, sharp@0.34.5); avoid broadening it to ranges or
additional packages, and ensure any future updates are paired with a manual
security/advisory review before changing this list.
app/[locale]/checkout/page.module.css (2)

62-70: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Revisar el color de texto en .warning.

El bloque usa border-color: var(--color-warning) pero el texto usa var(--color-green-dark), lo que resulta semánticamente inconsistente para un mensaje de advertencia. Verificar si se pretendía usar una variable de advertencia (p. ej. --color-warning-dark o similar) en lugar de un tono verde.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/checkout/page.module.css around lines 62 - 70, The `.warning`
style uses a green text color that is inconsistent with its warning border, so
update the text color in the checkout page CSS to a warning-appropriate token
instead of `var(--color-green-dark)`. Locate the `.warning` rule in
`page.module.css` and align its `color` with the existing warning palette, using
the corresponding warning/darker warning variable if available so the message
reads consistently with the border styling.

13-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Nombres de clase en camelCase (kebab-case esperado por Stylelint).

Stylelint señala que .checkoutCard, .sellerSection, .sellerName e .itemCustomizationRemoved no siguen kebab-case, pero esto coincide con el patrón camelCase ya usado en el resto de clases del archivo (p. ej. .itemCustomization, .totals), por lo que no es una regresión introducida por este cambio.

Also applies to: 19-19, 24-24, 56-56

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/checkout/page.module.css at line 13, The CSS module selectors
in page.module.css use camelCase names like checkoutCard, sellerSection,
sellerName, and itemCustomizationRemoved, which triggers Stylelint’s kebab-case
rule. Update these class selectors to the project’s expected kebab-case naming
and keep the corresponding className references in sync wherever these symbols
are used, or otherwise align the lint rule with the established convention in
this file.

Source: Linters/SAST tools

modules/cart/presentation/components/add-to-cart-button.tsx (1)

632-632: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Texto incorrecto: se reutiliza labels.adding para el estado de guardado de diseño.

labels.adding está destinado al flujo de "añadir al carrito" (ver uso en feedbackLabel, línea 577). Reutilizarlo aquí para savingDesign mostrará un texto semánticamente incorrecto (p. ej. "Añadiendo..." mientras se guarda un diseño, no se añade al carrito).

💬 Ejemplo de ajuste propuesto
 interface CartButtonLabels {
   addToCart: string;
   removeFromCart: string;
   adding: string;
   added: string;
   error: string;
+  savingDesign?: string;
   ...
 }
-            {savingDesign ? labels.adding : labels.saveDesign}
+            {savingDesign ? (labels.savingDesign ?? labels.adding) : labels.saveDesign}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/cart/presentation/components/add-to-cart-button.tsx` at line 632, The
`add-to-cart-button` render branch is reusing `labels.adding` for the
`savingDesign` state, which shows the wrong copy for a design save flow. Update
the conditional text in `AddToCartButton` so `savingDesign` uses a dedicated
save-design label/message instead of the add-to-cart label, and keep
`labels.adding` only for the actual cart-adding path (as already reflected in
`feedbackLabel`).
modules/cart/presentation/components/cart-view.tsx (1)

8-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inconsistencia: mezcla de labels (props) y useDictionary() (contexto) en el mismo componente.

CartView recibe todos sus textos vía el prop labels, pero ahora estas nuevas cadenas (customizationDesignImageAlt, customizationColor, price) se obtienen directamente del contexto con useDictionary(). Esto introduce una segunda fuente de traducción en un componente que hasta ahora era completamente controlado por props, dificultando pruebas unitarias (requieren DictionaryProvider o dependen del fallback a español) y la coherencia del componente.

Sugerencia: añadir estas claves a CartViewLabels y pasarlas desde el padre, igual que el resto de textos.

Also applies to: 130-130, 303-303, 323-323, 356-358

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/cart/presentation/components/cart-view.tsx` at line 8, `CartView` is
mixing two translation sources by using `useDictionary()` for a few new strings
while the rest still comes from the `labels` prop. Update `CartViewLabels` to
include `customizationDesignImageAlt`, `customizationColor`, and `price`, then
pass them in from the parent so `CartView` stays fully prop-driven. Remove the
`useDictionary()` dependency from `CartView` and replace the affected usages
with `labels` fields, keeping the component consistent and easier to test.
app/[locale]/orders/[orderId]/page.tsx (1)

66-73: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

El código 'cat' no es el subtag BCP-47 canónico para catalán.

new Date(order.createdAt).toLocaleDateString(locale) recibe locale tal cual (puede ser 'cat'). El código ISO 639-1 de catalán es ca; cat es sintácticamente válido para Intl pero no está registrado como subtag canónico, por lo que el formato de fecha resultante puede no corresponder realmente a las convenciones catalanas (comportamiento dependiente del motor JS). Conviene mapear 'cat''ca' antes de pasarlo a toLocaleDateString.

🌍 Fix sugerido
+const INTL_LOCALE_MAP: Record<string, string> = { es: 'es', cat: 'ca' };
...
-              {new Date(order.createdAt).toLocaleDateString(locale)}
+              {new Date(order.createdAt).toLocaleDateString(INTL_LOCALE_MAP[locale] ?? locale)}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/orders/[orderId]/page.tsx around lines 66 - 73, The date
formatting in the order page uses locale directly in the `order.createdAt`
display, so normalize the locale before calling `toLocaleDateString`. Update the
`page.tsx` logic around the `order.createdAt` render to map the non-canonical
`'cat'` locale to the canonical `'ca'` code, and pass that normalized value to
`new Date(order.createdAt).toLocaleDateString(...)` so Catalan dates use the
correct locale behavior.
prisma/schema.prisma (1)

82-83: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

El índice sobre name no acelera las búsquedas contains.

El nuevo filtro q usa name: { contains: filter.q }, que PostgreSQL traduce a LIKE '%valor%'. Un índice B-tree estándar no sirve para patrones con comodín inicial; solo ayuda con LIKE 'valor%'. Para que la búsqueda escale con el catálogo, considera un índice GIN con pg_trgm.

@@index([name(ops: raw("gin_trgm_ops"))], type: Gin)

(requiere CREATE EXTENSION IF NOT EXISTS pg_trgm; en una migración)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@prisma/schema.prisma` around lines 82 - 83, The current `@@index([name])` in
the Prisma schema does not help the new `q` search path because `name: {
contains: filter.q }` becomes a leading-wildcard match. Update the `Product`
model indexing strategy to use a trigram-based GIN index on `name` instead of
the plain B-tree index, and ensure the schema references the appropriate
`pg_trgm` operator class so `contains` queries in the search/filter logic scale
better with the catalog.
modules/orders/infrastructure/prisma-order-repository.ts (1)

47-100: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicación entre findPaginated y findById.

El bloque translationLocale, el include de product.translations/images, y el mapeo de lineItems (productName, productImageUrl, unitPrice) están duplicados casi de forma idéntica entre ambos métodos. Extraer un helper privado (p. ej. buildLineItemInclude(locale) y mapLineItem(item)) reduciría el riesgo de que ambos métodos diverjan al corregir el fallback de traducción u otros ajustes futuros.

Also applies to: 187-232

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/orders/infrastructure/prisma-order-repository.ts` around lines 47 -
100, The `findPaginated` and `findById` paths in `PrismaOrderRepository`
duplicate the same `translationLocale`, product `include`, and line-item mapping
logic, which risks the two methods drifting apart. Extract shared private
helpers in this repository, such as a helper that builds the product include for
a locale and a helper that maps a line item into the response shape, then have
both `findPaginated` and `findById` use them so the translation fallback and
image/unit-price handling stay consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/`[locale]/orders/[orderId]/page.module.css:
- Around line 17-134: The new CSS module classes in this order page are using
camelCase for TSX access, but `selector-class-pattern` is still enforcing
kebab-case and will fail lint. Update the Stylelint configuration to allow
camelCase selectors in `*.module.css` (preferred for `styles.itemRow`-style
usage), or rename the selectors and corresponding references in `page.tsx`
consistently. Use the module classes like `detailRow`, `itemsCard`, `itemRow`,
and `retryButton` to locate the affected styling.

In `@app/`[locale]/orders/[orderId]/page.tsx:
- Around line 88-90: The line total calculation in the order item rendering is
using a truthy check on item.unitPrice, which treats 0 as missing and hides
valid free-item totals. Update the logic in the page component where lineTotal
is derived so it checks explicitly for a defined numeric value (for example,
against null/undefined) before multiplying by item.quantity, keeping the
undefined fallback only when the price is actually absent.

In `@app/`[locale]/orders/page.tsx:
- Around line 181-183: The empty-state Card in the orders page is using the
wrong localization key and currently shows the page title instead of the
no-orders message. Update the fallback in the orders empty-state rendering
within the page component so it uses the dedicated empty-state text key from the
orders dictionary rather than dict.orders?.myOrders, keeping the same Card
structure but swapping to the proper “No orders yet” message source.

In `@app/`[locale]/seller/orders/page.module.css:
- Line 14: The CSS module class selectors use camelCase and are failing
selector-class-pattern; rename the affected classes in page.module.css to
kebab-case and update all corresponding references in page.tsx to use the new
names via styles[...] access. If these CSS Modules are intended to keep
camelCase, instead adjust the Stylelint selector-class-pattern rule to allow
them, but keep the naming consistent across the module and the page component.

In `@app/`[locale]/seller/orders/page.tsx:
- Around line 178-206: The filter labels in the seller orders page are not
associated with their select controls, so update the form in the orders page to
connect each <label> with its corresponding <select> by using matching
identifiers (or by nesting the control inside the label). Apply this to the
status and sort controls in the filter form so the labels for
dict.orders?.filterStatus and dict.orders?.sortBy are properly tied to the
selects.
- Around line 240-241: The empty-state in the seller orders page is using the
page title key instead of the dedicated no-results message, so update the render
in the seller orders component to use the empty-state translation key from the
orders dictionary rather than sellerOrdersTitle. Locate the empty-state branch
in the page component that renders the Card and switch it to the correct key so
the message shows “No orders yet” or its localized equivalent.

In `@app/api/orders/`[orderId]/status/route.ts:
- Around line 63-66: The redirect in the order status route is using the
client-controlled referer directly, which can cause an open redirect after the
authenticated POST. Update the logic in the status handler to validate the
referer in the request flow before calling NextResponse.redirect, allowing only
same-origin URLs (or otherwise falling back to a safe JSON response) and keep
the change localized around the referer handling in the route.

In `@modules/orders/domain/entities/order-line-item.ts`:
- Around line 14-19: Persist the checkout snapshot for product images by wiring
productImageUrl through the order line item flow. Update buildLineItems so it
populates productImageUrl from the source product data when constructing line
items, and update saveOrderLineItems to persist that field when creating
records. Make sure the OrderLineItem entity and the save/build logic stay
aligned so productImageUrl is stored and later usable as the fallback.

In `@modules/orders/infrastructure/prisma-order-repository.ts`:
- Line 58: La relación de imágenes en Prisma está limitando a una sola (`images:
{ take: 1 }`) pero sin orden determinista, así que la “primera” imagen puede
variar entre consultas. Actualiza el include en `PrismaOrderRepository` para que
la consulta de `images` use un `orderBy` explícito y estable (por ejemplo
`isPrimary`, `createdAt` u otro campo de prioridad), manteniendo `take: 1` para
que siempre se devuelva la misma imagen esperada.
- Around line 33-45: The q filter in prisma-order-repository’s where.lineItems
query is currently case-sensitive, so searches can miss matches like lowercase
and uppercase variants. Update the name.contains condition in the order
repository query to use case-insensitive matching, and keep the fix localized to
the filter.q branch in the repository logic that builds the Prisma where clause.

In `@modules/orders/presentation/schemas/order-schemas.ts`:
- Line 16: The search field q in the order schema is currently passed through
without normalization, so update the Zod definition in the order-schemas schema
to trim whitespace and cap the maximum length before it reaches the Prisma
contains filter. Use the existing q field definition in the order schema to
apply the normalization and validation there, so only a cleaned, reasonably
sized search term is accepted.

In `@prisma/schema.prisma`:
- Around line 178-179: The new unitPrice default in the Prisma schema can leave
historical order lines with invalid $0 pricing because findPaginated and
findById now read unitPrice directly and the UI uses it to compute line totals.
Update the schema/migration flow around unitPrice to backfill existing rows
before relying on the default, using an appropriate source such as
Product.basePrice or a per-order allocation from Order.total and quantity so
preexisting orders keep meaningful line amounts.

In `@shared/ui/tag-list.tsx`:
- Around line 19-26: The fallback labels in TagList are pointing to the wrong
dictionary keys, so the defaults resolve to undefined. Update the TagList
component to use existing dictionary entries from useDictionary for
addLabelValue and emptyLabelValue, and verify the fallback properties come from
the correct nested sections rather than dict.common. Keep the fix localized in
TagListProps handling so the component still prefers passed-in labels and only
falls back to valid dictionary values.

---

Outside diff comments:
In `@modules/orders/infrastructure/prisma-order-repository.ts`:
- Around line 163-185: `saveOrderLineItems` no está persistiendo
`productImageUrl`, así que el snapshot de imagen queda perdido y los lectores
posteriores caen en el fallback de la imagen actual del producto. Actualiza el
payload del `tx.orderLineItem.createMany` para incluir `productImageUrl` desde
`OrderLineItemEntity`, y verifica que los puntos que construyen esos entities,
como `create-order-use-case` y `handle-cart-checked-out`, asignen ese campo al
momento del checkout.

In `@shared/ui/tag-list.tsx`:
- Around line 94-101: The remove button in TagList does not announce its action
clearly because aria-label only uses the tag value. Update the button in
tag-list.tsx to use an action-oriented accessible label in the TagList render
logic, combining the remove action with the tag name (similar to
dict.common.removeFromCart) so screen readers hear that the button removes that
specific tag.

---

Nitpick comments:
In `@app/`[locale]/checkout/page.module.css:
- Around line 62-70: The `.warning` style uses a green text color that is
inconsistent with its warning border, so update the text color in the checkout
page CSS to a warning-appropriate token instead of `var(--color-green-dark)`.
Locate the `.warning` rule in `page.module.css` and align its `color` with the
existing warning palette, using the corresponding warning/darker warning
variable if available so the message reads consistently with the border styling.
- Line 13: The CSS module selectors in page.module.css use camelCase names like
checkoutCard, sellerSection, sellerName, and itemCustomizationRemoved, which
triggers Stylelint’s kebab-case rule. Update these class selectors to the
project’s expected kebab-case naming and keep the corresponding className
references in sync wherever these symbols are used, or otherwise align the lint
rule with the established convention in this file.

In `@app/`[locale]/orders/[orderId]/page.tsx:
- Around line 66-73: The date formatting in the order page uses locale directly
in the `order.createdAt` display, so normalize the locale before calling
`toLocaleDateString`. Update the `page.tsx` logic around the `order.createdAt`
render to map the non-canonical `'cat'` locale to the canonical `'ca'` code, and
pass that normalized value to `new
Date(order.createdAt).toLocaleDateString(...)` so Catalan dates use the correct
locale behavior.

In `@app/`[locale]/products/[id]/mockup-canvas-control.tsx:
- Around line 292-296: The String.fromCharCode change in useMemo for
positionReadout is unnecessary because the i18next/no-literal-string rule only
targets JSX text, not this template literal. Revert the expression in
mockup-canvas-control.tsx to simple literal punctuation while keeping the same
positionReadout calculation, and use the useMemo block as the place to restore
readability.

In `@modules/cart/presentation/components/add-to-cart-button.tsx`:
- Line 632: The `add-to-cart-button` render branch is reusing `labels.adding`
for the `savingDesign` state, which shows the wrong copy for a design save flow.
Update the conditional text in `AddToCartButton` so `savingDesign` uses a
dedicated save-design label/message instead of the add-to-cart label, and keep
`labels.adding` only for the actual cart-adding path (as already reflected in
`feedbackLabel`).

In `@modules/cart/presentation/components/cart-view.tsx`:
- Line 8: `CartView` is mixing two translation sources by using
`useDictionary()` for a few new strings while the rest still comes from the
`labels` prop. Update `CartViewLabels` to include `customizationDesignImageAlt`,
`customizationColor`, and `price`, then pass them in from the parent so
`CartView` stays fully prop-driven. Remove the `useDictionary()` dependency from
`CartView` and replace the affected usages with `labels` fields, keeping the
component consistent and easier to test.

In `@modules/orders/infrastructure/prisma-order-repository.ts`:
- Around line 47-100: The `findPaginated` and `findById` paths in
`PrismaOrderRepository` duplicate the same `translationLocale`, product
`include`, and line-item mapping logic, which risks the two methods drifting
apart. Extract shared private helpers in this repository, such as a helper that
builds the product include for a locale and a helper that maps a line item into
the response shape, then have both `findPaginated` and `findById` use them so
the translation fallback and image/unit-price handling stay consistent.

In `@package.json`:
- Around line 118-123: Review the allowScripts whitelist in package.json and
keep it narrowly scoped to the exact native packages and versions already
intended here (`@prisma/engines`@7.8.0, prisma@7.8.0, bcrypt@6.0.0,
esbuild@0.28.1, sharp@0.34.5); avoid broadening it to ranges or additional
packages, and ensure any future updates are paired with a manual
security/advisory review before changing this list.

In `@prisma/schema.prisma`:
- Around line 82-83: The current `@@index([name])` in the Prisma schema does not
help the new `q` search path because `name: { contains: filter.q }` becomes a
leading-wildcard match. Update the `Product` model indexing strategy to use a
trigram-based GIN index on `name` instead of the plain B-tree index, and ensure
the schema references the appropriate `pg_trgm` operator class so `contains`
queries in the search/filter logic scale better with the catalog.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b5ae0eee-68e7-48bb-a483-a0e92432943b

📥 Commits

Reviewing files that changed from the base of the PR and between aafae7a and a2e58b5.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (48)
  • app/[locale]/checkout/page.module.css
  • app/[locale]/checkout/page.tsx
  • app/[locale]/layout.tsx
  • app/[locale]/orders/[orderId]/page.module.css
  • app/[locale]/orders/[orderId]/page.tsx
  • app/[locale]/orders/page.module.css
  • app/[locale]/orders/page.tsx
  • app/[locale]/products/[id]/mockup-canvas-control.tsx
  • app/[locale]/products/[id]/page.tsx
  • app/[locale]/seller/orders/page.module.css
  • app/[locale]/seller/orders/page.tsx
  • app/api/orders/[orderId]/status/route.ts
  • eslint.config.mjs
  • modules/cart/presentation/components/add-to-cart-button.tsx
  • modules/cart/presentation/components/cart-popup.tsx
  • modules/cart/presentation/components/cart-view.tsx
  • modules/cart/presentation/components/checkout-confirm-button.module.css
  • modules/cart/presentation/components/checkout-confirm-button.tsx
  • modules/orders/application/create-order-use-case.ts
  • modules/orders/application/handle-cart-checked-out.ts
  • modules/orders/application/list-customer-orders-use-case.ts
  • modules/orders/application/list-seller-orders-use-case.ts
  • modules/orders/domain/entities/order-line-item.ts
  • modules/orders/domain/order-repository.ts
  • modules/orders/infrastructure/prisma-order-repository.ts
  • modules/orders/presentation/schemas/order-schemas.ts
  • modules/products/presentation/components/product-customization-config-editor.tsx
  • package.json
  • prisma/migrations/20260705193304_add_order_line_item_unit_price/migration.sql
  • prisma/schema.prisma
  • shared/i18n/locales/cat.json
  • shared/i18n/locales/es.json
  • shared/layout/user-menu-dropdown.tsx
  • shared/ui/price-field.tsx
  • shared/ui/status-badge.module.css
  • shared/ui/tag-list.tsx
  • tests/integration/orders.integration.test.ts
  • tests/integration/orders/prisma-order-repository.integration.test.ts
  • tests/unit/app/[locale]/orders/[orderId]/page.test.tsx
  • tests/unit/app/[locale]/orders/page.test.tsx
  • tests/unit/app/[locale]/seller/orders/page.test.tsx
  • tests/unit/app/checkout/checkout-confirm-button.test.tsx
  • tests/unit/modules/orders/application/assign-to-production-use-case.test.ts
  • tests/unit/modules/orders/application/mark-as-paid-use-case.test.ts
  • tests/unit/modules/presentation/components/cart-popup.test.tsx
  • tests/unit/modules/presentation/components/user-menu-dropdown.test.tsx
  • verify-report-cart-pr1.md
  • verify-report-customizations-pr4.md
💤 Files with no reviewable changes (4)
  • verify-report-customizations-pr4.md
  • modules/products/presentation/components/product-customization-config-editor.tsx
  • tests/unit/modules/presentation/components/user-menu-dropdown.test.tsx
  • verify-report-cart-pr1.md

Comment on lines +17 to +134
.detailRow {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-sm) 0;
}

.detailRow + .detailRow {
border-top: 1px solid var(--color-border);
}

.detailLabel {
color: var(--color-text-secondary);
font-size: 0.9rem;
}

.detailValue {
font-weight: var(--font-weight-medium);
font-size: 0.95rem;
}

.itemsCard {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
}

.itemsTitle {
margin: 0;
font-size: 1.05rem;
font-weight: var(--font-weight-semibold);
color: var(--color-green-dark);
}

.itemsList {
display: flex;
flex-direction: column;
}

.itemRow {
display: flex;
align-items: center;
gap: var(--spacing-md);
padding: var(--spacing-sm) 0;
}

.itemRow + .itemRow {
border-top: 1px solid var(--color-border);
}

.itemImage {
border-radius: var(--radius-sm);
object-fit: cover;
flex-shrink: 0;
}

.itemInfo {
display: flex;
flex-direction: column;
gap: 0.15rem;
min-width: 0;
flex: 1;
}

.itemName {
font-weight: var(--font-weight-medium);
font-size: 0.95rem;
}

.itemCustomization {
font-size: 0.8rem;
color: var(--color-text-secondary);
}

.itemRight {
display: flex;
align-items: center;
gap: var(--spacing-sm);
flex-shrink: 0;
}

.itemQty {
color: var(--color-text-secondary);
font-size: 0.9rem;
}

.itemLineTotal {
font-weight: var(--font-weight-semibold);
font-size: 0.95rem;
min-width: 5rem;
text-align: right;
}

.noItems {
color: var(--color-text-secondary);
font-size: 0.9rem;
margin: 0;
}

.retrySection {
margin: 0;
}

.retryButton {
width: 100%;
padding: 0.75rem;
background: var(--color-coral);
color: var(--color-white);
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 1rem;
font-weight: var(--font-weight-medium);
}

.retryButton:hover {
opacity: 0.85;
}

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.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Conflicto entre convención camelCase y la regla Stylelint selector-class-pattern.

Todas las clases nuevas de este módulo (.detailRow, .itemsCard, .itemRow, .retryButton, etc.) usan camelCase para permitir el acceso styles.itemRow en el TSX, pero Stylelint las marca como error por no ser kebab-case. Esto probablemente hará fallar el paso de lint en CI. Dado que este PR trata justamente de "ajustes eslint" (ver PR objectives), conviene resolverlo: o se ajusta selector-class-pattern en la config de Stylelint para permitir camelCase en archivos *.module.css, o se renombran las clases a kebab-case y se actualiza el acceso en page.tsx (styles['detail-row']).

🔧 Opción recomendada: excepción en la configuración de Stylelint
 // stylelint.config.js (ejemplo)
 rules: {
-  'selector-class-pattern': '^[a-z][a-zA-Z0-9]*$',
+  'selector-class-pattern': [
+    '^[a-z][a-zA-Z0-9]*$',
+    { message: 'Se espera camelCase para CSS Modules' },
+  ],
 }
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 17-17: Expected class selector ".detailRow" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 24-24: Expected class selector ".detailRow" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 24-24: Expected class selector ".detailRow" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 28-28: Expected class selector ".detailLabel" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 33-33: Expected class selector ".detailValue" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 38-38: Expected class selector ".itemsCard" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 44-44: Expected class selector ".itemsTitle" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 51-51: Expected class selector ".itemsList" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 56-56: Expected class selector ".itemRow" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 63-63: Expected class selector ".itemRow" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 63-63: Expected class selector ".itemRow" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 67-67: Expected class selector ".itemImage" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 73-73: Expected class selector ".itemInfo" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 81-81: Expected class selector ".itemName" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 86-86: Expected class selector ".itemCustomization" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 91-91: Expected class selector ".itemRight" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 98-98: Expected class selector ".itemQty" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 103-103: Expected class selector ".itemLineTotal" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 110-110: Expected class selector ".noItems" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 116-116: Expected class selector ".retrySection" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 120-120: Expected class selector ".retryButton" to be kebab-case (selector-class-pattern)

(selector-class-pattern)


[error] 132-132: Expected class selector ".retryButton" to be kebab-case (selector-class-pattern)

(selector-class-pattern)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/orders/[orderId]/page.module.css around lines 17 - 134, The new
CSS module classes in this order page are using camelCase for TSX access, but
`selector-class-pattern` is still enforcing kebab-case and will fail lint.
Update the Stylelint configuration to allow camelCase selectors in
`*.module.css` (preferred for `styles.itemRow`-style usage), or rename the
selectors and corresponding references in `page.tsx` consistently. Use the
module classes like `detailRow`, `itemsCard`, `itemRow`, and `retryButton` to
locate the affected styling.

Source: Linters/SAST tools

Comment thread app/[locale]/orders/[orderId]/page.tsx Outdated
Comment thread app/[locale]/orders/page.tsx
margin-bottom: var(--spacing-lg);
}

.headerLead {

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.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Alinea los selectores con selector-class-pattern.

Stylelint está rechazando las clases camelCase de este CSS module. Renómbralas a kebab-case y actualiza las referencias en page.tsx, o ajusta la regla si CSS Modules deben permitir camelCase.

Ejemplo de cambio
-.headerLead {
+.header-lead {

Luego referenciarla como styles['header-lead'].

Also applies to: 29-29, 40-40, 46-46, 54-54, 65-65, 77-77, 81-81, 87-87, 93-93, 100-100, 104-104, 116-116, 120-120, 124-124

🧰 Tools
🪛 Stylelint (17.14.0)

[error] 14-14: Expected class selector ".headerLead" to be kebab-case (selector-class-pattern)

(selector-class-pattern)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/`[locale]/seller/orders/page.module.css at line 14, The CSS module class
selectors use camelCase and are failing selector-class-pattern; rename the
affected classes in page.module.css to kebab-case and update all corresponding
references in page.tsx to use the new names via styles[...] access. If these CSS
Modules are intended to keep camelCase, instead adjust the Stylelint
selector-class-pattern rule to allow them, but keep the naming consistent across
the module and the page component.

Source: Linters/SAST tools

Comment thread app/[locale]/seller/orders/page.tsx
Comment thread modules/orders/infrastructure/prisma-order-repository.ts
Comment thread modules/orders/infrastructure/prisma-order-repository.ts Outdated
Comment thread modules/orders/presentation/schemas/order-schemas.ts Outdated
Comment thread prisma/schema.prisma
Comment on lines +178 to +179
unitPrice Decimal @default(0)
productImageUrl String?

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

unitPrice con DEFAULT 0 sin backfill corrompe datos históricos.

La migración añade unitPrice con valor por defecto 0 para las filas existentes, sin backfill. Según el contexto (findPaginated/findById mapean unitPrice: Number(item.unitPrice) y las páginas rediseñadas calculan el total por línea con unitPrice * quantity), todos los pedidos creados antes de este cambio mostrarán $0 en el importe de sus líneas, aunque el total del pedido (Order.total) siga siendo correcto.

Considera un backfill (ej. aproximar unitPrice a partir de Product.basePrice o de order.total / SUM(quantity) por pedido) antes de asumir el default 0 como válido para datos preexistentes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@prisma/schema.prisma` around lines 178 - 179, The new unitPrice default in
the Prisma schema can leave historical order lines with invalid $0 pricing
because findPaginated and findById now read unitPrice directly and the UI uses
it to compute line totals. Update the schema/migration flow around unitPrice to
backfill existing rows before relying on the default, using an appropriate
source such as Product.basePrice or a per-order allocation from Order.total and
quantity so preexisting orders keep meaningful line amounts.

Comment thread shared/ui/tag-list.tsx Outdated
@henri318 henri318 merged commit 79a2c95 into main Jul 6, 2026
3 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.

1 participant