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: Step1 → ReturnConditions + 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 mapeoen→gb,fr→fr,de→de,it→it,pt→pt,el→gry 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 deForm.error.*. - Al enviar,
POST /api/get-ordercon{ order, email }. Si la respuesta traeerror, se muestra un toast y no se avanza. Si es correcta, el pedido se guarda enOrderContexty se pasa al paso 2.
Paso 2 — Seleccionar artículos y motivo
Componentes: Step2 → OrderResume + Step2ReturnForm → ReturnItem → ReasonSelector.
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
useReturnReasonscarga los motivos una sola vez desdeGET /api/get-return-reasons(solo los que tienenenabled === true).- Cada artículo seleccionado exige un motivo. Si el motivo tiene
allow_extra_info, se habilita unTextareapara 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: Step3 → Step3ReturnMethodForm.
- Los métodos los provee
ReturnMethodContext, que llama aPOST /api/get-return-methodscon el país del pedido y descarta los que no están habilitados. Cada método se traduce al idioma activo conmapReturnMethod(título, descripción e instrucciones vienen en el campotranslationsde 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) simethod.price >= subtotal: el coste de la devolución no puede igualar o superar el importe de los artículos. - Si el método es
pricelessno 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.gettingReturnMethodsy un botón de reinicio. - Al continuar se exige un método seleccionado (
Form.error.shippingMethod.required) y se guarda consetReturnMethod.
Paso 4 — Resumen y confirmación
Componentes: Step4 → ReturnResume + AlertDialog.
- Muestra el resumen final con importes formateados según
localeycurrencydel pedido. - El botón Continuar no envía nada: abre un
AlertDialogde confirmación (onBeforeContinuedevuelvefalse). - Al confirmar,
POST /api/set-returncon 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 enRefundContextjunto concreatedy se avanza al paso 5. Si no, se muestra el toastStep4.error.
Paso 5 — Confirmación (ThankYouPage)
- Muestra el número de devolución (
HWRETxxxxx), número de pedido, fecha, nombre y email. - Genera un QR del número de pedido en cliente (
next-qrcode). - Pide la URL firmada del PDF a
POST /api/get-pdf-urly la expone como enlace de descarga (ver PDFs y URLs firmadas). - Una vez tiene la URL del PDF, dispara una sola vez (
journeyTriggered) el journey de Marketing Cloud víaPOST /api/trigger-journey, enviando entre otros:Country__c,EmailAddress,FirstName,items(JSON),OrderNumber,pdfUrl(prefijado conhttps://returns.hawkersco.com),Return_date,Return_method(JSON),Return_number,shippingCost,SubscriberKey(=sfsc_user_id) yTotalFinal. - Renderiza las instrucciones del método de devolución tal cual vienen de robo-api, mediante
dangerouslySetInnerHTML(HTML gestionado internamente, ver Notas técnicas). - El botón final destruye todo el estado y vuelve al paso 1.
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.