Cómo construir un dashboard de administración de e-commerce en Next.js: pedidos, reembolsos e inventario
Lo difícil de un admin de tienda en Next.js: dos estados de pedido, fallos masivos parciales, reembolsos exactos al céntimo, ajustes de inventario y descuentos.
Preet Suthar · · 6 min de lectura
- Next.js
- React
- E-commerce
- Dashboard
Un admin de e-commerce parece una app CRUD: una tabla de pedidos, una página por pedido, un formulario por producto. Las tablas son lo fácil. Lo difícil es que cada número de un admin de tienda es dinero o stock, y ambos tienen que seguir siendo correctos mientras los clientes siguen comprando en segundo plano.
Esto es lo que aprendí construyendo la parte de administración de una tienda en Next.js: tablas de pedidos con estados honestos, una página de pedido que muestra lo que queda por hacer, reembolsos que cuadran al céntimo, ediciones de inventario que sobreviven a los pedidos en vivo y descuentos que los comerciantes pueden leer.
Pedidos: dos estados, no uno
El primer error es una única columna status. Un pedido tiene al menos dos estados independientes: el pago (pendiente, autorizado, pagado, reembolsado parcialmente, reembolsado) y la preparación (sin preparar, preparado parcialmente, preparado). "Pagado y sin preparar" es la lista con la que un comerciante trabaja cada mañana, y una sola columna de estado no puede expresarlo.
Muéstralos como dos píldoras. No dejes que el color cargue con el significado. WCAG pide que el color no sea la única forma de mostrar la información, y "Pagado parcialmente" en naranja claro junto a "Pagado" en verde es difícil de distinguir para mucha gente. Una etiqueta de texto más un pequeño glifo, un círculo vacío, medio lleno o lleno, se lee de un vistazo.
Las vistas guardadas viven en la URL
Los comerciantes no navegan por los pedidos, trabajan con listas: Sin preparar, Sin pagar, Abiertos. Una vista guardada no es más que filtros, un orden y una búsqueda. Guarda la consulta entera en la URL para que un enlace abra la misma lista y Atrás funcione, y léela en el servidor en Next.js:
Cuando los filtros actuales difieran de la vista guardada, marca la pestaña como modificada y ofrece Restablecer, Actualizar vista y Guardar como vista nueva, igual que una hoja de cálculo te avisa de que hay un filtro activo.
Los pedidos nuevos no deben mover la fila bajo el cursor
Los pedidos llegan mientras el comerciante mira la lista. Si las filas nuevas empujan todo hacia abajo, la casilla en la que iba a hacer clic se convierte en otro pedido. Mientras una fila tenga el foco o haya algo seleccionado, retén los pedidos nuevos tras una píldora "1 pedido nuevo · Mostrar" e insértalos arriba solo cuando no haya nada en curso.
Las acciones masivas fallan a medias
"Marcar 6 pedidos como preparados" a veces preparará 5. Uno está en espera, o se canceló hace un minuto. Diseña la API para ello: devuelve los ids que fallaron, cada uno con un motivo, e informa exactamente de eso.
El mensaje se escribe solo: "5 pedidos preparados. 1 no se pudo preparar: #1031 está en espera", con un enlace a ese pedido. Un toast de error de todo o nada dejaría al comerciante adivinando cuáles se procesaron.
La página del pedido muestra lo que falta
En la página del pedido, separa los artículos según lo que les pasó, no según cómo se compraron. Cada envío tiene su propia tarjeta con el transportista y el seguimiento. Los artículos reembolsados tienen la suya. Y la tarjeta que más importa es lo que aún queda por enviar:
El historial de debajo debe construirse a partir de los eventos del pedido, con títulos cortos sacados de los datos ("Pago de $564.41 capturado", "Enviado con UPS" con el número de seguimiento enlazado), la hora exacta en un tooltip y los eventos repetidos del mismo tipo agrupados en una línea. Los comentarios del equipo van en el mismo feed y se publican de forma optimista, con un reintento si fallan.
Las cuentas del reembolso tienen que cuadrar al céntimo
Los reembolsos son donde el código de un admin calcula mal el dinero con más frecuencia. Un cliente compró tres artículos con un descuento de $10 en el pedido e impuestos, y devuelve uno. ¿Cuánto recupera?
Reparte los importes del pedido entre las líneas en proporción al precio de cada una. Los descuentos según el precio, los impuestos según el precio después del descuento. Repartir en céntimos enteros deja restos, y el método del resto mayor los distribuye para que las partes siempre sumen el original:
allocate(1000, [3000, 3000, 3000]) da [334, 333, 333], que suma exactamente 1000.
Hay una segunda trampa. Si hoy reembolsas una unidad de una línea y las otras dos la semana que viene, los totales deben coincidir con reembolsar las tres a la vez. Da a cada unidad una porción acumulada de la parte de la línea en lugar de redondear cada reembolso por separado:
from es cuántas unidades se reembolsaron ya. Se repartan como se repartan los reembolsos, las porciones suman lineShare.
El resto de la pantalla de reembolso es gestión de estados. El envío se puede reembolsar hasta lo pagado menos lo ya reembolsado. El dinero vuelve a los métodos de pago en un orden fijo cuando un pedido se pagó entre una tarjeta regalo y una tarjeta. Envía una clave de idempotencia para que una petición reintentada nunca reembolse dos veces, pide confirmación en los reembolsos grandes y muestra "pendiente en el banco" como un estado propio, no como éxito ni como fallo.
Inventario: ajusta, no sobrescribas
El editor de inventario ingenuo envía el número nuevo: "Sand / Large tiene 47". Si entraron dos pedidos mientras el comerciante escribía, guardar 47 los deshace sin avisar.
Envía los cambios como ajustes. "Recibidas 5" es un delta, y los deltas se pueden aplicar con seguridad sobre cualquier cosa que haya pasado entretanto. Cuando el comerciante de verdad quiere fijar un recuento, "Fijar en 47" tras un inventario cíclico, conviértelo en un delta respecto a la cantidad que vio y deja que el servidor lo rechace si esa cantidad cambió:
Una fila rechazada se queda en el borrador con un mensaje como "Otro dispositivo cambió la cantidad a 40", y Volver a aplicar la envía de nuevo sobre el número nuevo. La mutación inventoryAdjustQuantities de Shopify funciona igual con su campo changeFromQuantity.
Separa también el número. Las existencias son lo que hay en el almacén, pero el stock comprometido ya está vendido y el stock no disponible está dañado o retenido. Los comerciantes necesitan ver lo disponible por separado de las existencias, o venderán stock que ya se ha vendido.
Descuentos como una frase
Los formularios de descuentos suelen ser una larga lista de campos, y los comerciantes no saben qué han creado hasta que un cliente se queja. Escribe la oferta como una frase con partes editables, "Los clientes obtienen un 15% de descuento en Ring Vase y Glass Carafe", pon la compra mínima, la elegibilidad de clientes, los límites de uso, las combinaciones y la programación en filas plegadas que muestren su valor actual, y previsualiza un checkout de ejemplo con el descuento aplicado.
Avisa de las configuraciones arriesgadas, como un 100% de descuento sin límite de uso, y programa en la zona horaria de la tienda, no en la del navegador.
Una tienda, todas las páginas
La última pieza es la coherencia. Reembolsa un artículo y el estado de pago del pedido, su historial, el gasto total del cliente y el stock repuesto deben cambiar a la vez. Haz que cada mutación devuelva las entidades que cambió y actualiza o revalida cada vista que las muestre. Y bloquea la navegación cuando haya cambios sin guardar en productos, stock o descuentos, incluido el botón Atrás del navegador.
Los bloques de HextaUI para esto
Estos son los problemas en torno a los que están construidos los bloques de admin de HextaUI Pro. Orders Table tiene vistas guardadas en la URL, píldoras separadas de pago y preparación con glifos de progreso, y acciones masivas que informan de los fallos parciales. Refund hace sus cuentas en calculateRefund(order, selection), una función pura con tests que usa el método del resto mayor. Inventory guarda ajustes, no sobrescrituras, y deja que los comerciantes vuelvan a aplicar las filas rechazadas. Discount Builder edita la oferta como una frase junto a una vista previa del checkout en vivo.
Commerce Dashboard los junta con Order Detail, Product Editor y Sales Overview en un único admin de tienda sobre un store compartido, con rutas hash por defecto o tus rutas de Next.js mediante su prop router, y ejemplos que conectan cada acción con la Shopify Admin API. La vista previa en vivo es el dashboard completo, así que puedes reembolsar un pedido y ver cómo se actualizan todas las páginas.
Preguntas frecuentes
¿Un pedido debe tener uno o dos estados? Al menos dos: el pago y la preparación son independientes, y los comerciantes filtran por combinaciones de ambos.
¿Cómo reparto un descuento entre los artículos reembolsados? En céntimos enteros, en proporción al precio de cada línea, usando el método del resto mayor para que las partes sumen exactamente.
¿Las ediciones de inventario deben enviar la nueva cantidad o el cambio? El cambio. Los deltas sobreviven a los pedidos realizados mientras el comerciante editaba; un recuento "fijar en" debe comprobarse contra la cantidad de la que partía.
¿Dónde deben vivir los filtros de un admin? En la URL, para que las vistas se puedan compartir, recargar y recorrer con Atrás.