Skip to main content

Flujo de devolución

El wizard vive en app/page.tsx y tiene 5 estados: los 4 pasos del Stepper más la pantalla de confirmación (paso 5, ThankYouPage). El paso actual se guarda en sessionStorage y en el historial del navegador.

flowchart LR
S1[1 · Buscar pedido<br/>nº pedido + email] --> S2[2 · Artículos<br/>+ motivo]
S2 --> S3[3 · Método de<br/>devolución]
S3 --> S4[4 · Resumen<br/>+ confirmación]
S4 -->|POST /api/set-return| S5[5 · Gracias<br/>HWRET… + PDF + journey]

Paso 1 — Localizar el pedido

Componentes: Step1ReturnConditions + FormSearchOrder.

  • A la izquierda se muestran las condiciones de devolución traducidas (ReturnConditions). En móvil (max-width: 768px) aparecen colapsadas tras un enlace ver más. La condición que enlaza con la página de contacto resuelve la URL según el idioma activo: https://www.hawkersco.com/<país>/page-contact.html, con el mapeo en→gb, fr→fr, de→de, it→it, pt→pt, el→gr y sin prefijo de país para el resto (español incluido).
  • A la derecha, el formulario pide número de pedido y email, validados con zod: ambos obligatorios (trim) y el email con formato válido. Los mensajes de error son claves de Form.error.*.
  • Al enviar, POST /api/get-order con { order, email }. Si la respuesta trae error, se muestra un toast y no se avanza. Si es correcta, el pedido se guarda en OrderContext y se pasa al paso 2.

Paso 2 — Seleccionar artículos y motivo

Componentes: Step2OrderResume + Step2ReturnFormReturnItemReasonSelector.

Elegibilidad del pedido

El pedido se considera devolvible si order.valid es true y existe al menos un artículo con returnable === true, in_process_return !== true y que no sea la línea de gastos de envío. Si no lo es, se muestra el texto Step2.invalidIntro en rojo y el único botón disponible es volver al inicio.

Elegibilidad por artículo

Un artículo es seleccionable cuando cumple: item.returnable && order.valid && !item.in_process_return && item.price > 0. Si no lo cumple, la tarjeta se muestra en gris con el texto Step2.inProcess (si ya tiene una devolución en curso) o Step2.notReturnable.

Los artículos se ordenan poniendo primero los devolvibles, y la línea cuyo SKU es S00233 se filtra de la lista porque representa los gastos de envío, no un producto (helper itemIsShippingCost).

Motivos

  • useReturnReasons carga los motivos una sola vez desde GET /api/get-return-reasons (solo los que tienen enabled === true).
  • Cada artículo seleccionado exige un motivo. Si el motivo tiene allow_extra_info, se habilita un Textarea para información adicional.
  • Existe seleccionar / deseleccionar todo cuando hay más de un artículo seleccionable.
  • Un panel lateral (Sheet) explica cómo se calculan los precios de devolución (productPrices.*).

Al continuar, Step2ReturnForm valida que haya al menos un artículo con motivo y guarda en RefundContext los artículos, orderId, los gastos de envío del pedido y el sfsc_user_id.

Paso 3 — Método de devolución

Componentes: Step3Step3ReturnMethodForm.

  • Los métodos los provee ReturnMethodContext, que llama a POST /api/get-return-methods con el país del pedido y descarta los que no están habilitados. Cada método se traduce al idioma activo con mapReturnMethod (título, descripción e instrucciones vienen en el campo translations de robo-api, que puede llegar como objeto o como string JSON).
  • Se renderizan como RadioGroup. Un método se muestra no disponible (badge rojo, no seleccionable) si method.price >= subtotal: el coste de la devolución no puede igualar o superar el importe de los artículos.
  • Si el método es priceless no se muestra precio; si su precio es 0 se muestra la etiqueta gratis.
  • Si el país no tiene ningún método habilitado, se muestra el error Step3.error.gettingReturnMethods y un botón de reinicio.
  • Al continuar se exige un método seleccionado (Form.error.shippingMethod.required) y se guarda con setReturnMethod.

Paso 4 — Resumen y confirmación

Componentes: Step4ReturnResume + AlertDialog.

  • Muestra el resumen final con importes formateados según locale y currency del pedido.
  • El botón Continuar no envía nada: abre un AlertDialog de confirmación (onBeforeContinue devuelve false).
  • Al confirmar, POST /api/set-return con el objeto de devolución completo. El route handler transforma el payload al formato de robo-api (SKU, precios, id_return_reason, extra_info, id_return_method, sfsc_user_id).
  • Si la respuesta trae return_name_id, se guarda en RefundContext junto con created y se avanza al paso 5. Si no, se muestra el toast Step4.error.

Paso 5 — Confirmación (ThankYouPage)

  1. Muestra el número de devolución (HWRETxxxxx), número de pedido, fecha, nombre y email.
  2. Genera un QR del número de pedido en cliente (next-qrcode).
  3. Pide la URL firmada del PDF a POST /api/get-pdf-url y la expone como enlace de descarga (ver PDFs y URLs firmadas).
  4. Una vez tiene la URL del PDF, dispara una sola vez (journeyTriggered) el journey de Marketing Cloud vía POST /api/trigger-journey, enviando entre otros: Country__c, EmailAddress, FirstName, items (JSON), OrderNumber, pdfUrl (prefijado con https://returns.hawkersco.com), Return_date, Return_method (JSON), Return_number, shippingCost, SubscriberKey (= sfsc_user_id) y TotalFinal.
  5. Renderiza las instrucciones del método de devolución tal cual vienen de robo-api, mediante dangerouslySetInnerHTML (HTML gestionado internamente, ver Notas técnicas).
  6. El botón final destruye todo el estado y vuelve al paso 1.
warning

Volver atrás con el navegador desde este paso cancela la sesión local (destroyOrder + destroyRefund + destroyStep): la devolución ya está creada en robo-api, pero el usuario deberá consultarla en /info con su HWRET… y su email.