MF-12 · herramientas · sep 2026

Mobile · herramientas

Calcula, consulta y encuentra tu sede.

La calculadora de costos, el catálogo de productos, Sedes Mogos y los tutoriales en el teléfono, a paridad con apps/client. La calculadora usa el mismo motor que la web y muestra la tarifa en una barra fija que abre el desglose. El catálogo ordena destacados y recientes en rieles y el resto por mes, y abre el PDF en el visor de MF-04. Las sedes copian dirección, receptor y tu Shipping Mark ya resuelta. Los tutoriales quedan construidos pero sin entrada, porque la web los escondió mientras no haya contenido. Rewards queda fuera.

Rutas
calculator · catalog · [id] · locations · tutorials · [id]
Paridad
estricta · web = verdad
Motor
shipping-price.ts · al centavo
Reuso
lists · PdfViewer · Sileo
Fuera
rewards · mapas · sesiones 1:1
Pantallas
25 a 390×844

01Por dónde se entra, que ya existe

mas.tsx · create-sheet.tsx · create-items.ts (MF-03)

Las puertas ya están cableadas desde MF-03 y apuntan a placeholders «Muy pronto» con owner="MF-12". Esta mega-fase no las toca, solo reemplaza lo que hay detrás. Catálogo y Sedes Mogos están en Más › Accesos. Calcular costo está en Crear › Otros, igual que en la web móvil. Tutoriales no tiene puerta: la web la quitó de Más (2026-08-09) y del menú de ayuda (2026-08-13, 26bf55375) porque no hay tutoriales publicados, y el test de Más de la app ya exige que no esté.

Más · Accesos Catálogo y Sedes Mogos, sin cambios
Crear · Otros «Calcular costo» abre la calculadora

Otras entradas: la notificación NEW_CATALOG trae actionUrl: /catalog/{id}, y web-route-map.ts suma /catalog/[id] → detalle para cuando MF-10 cablee los deep links. «Solicitar cotización» y «Hacer envío» de la calculadora llevan a MF-08 y MF-05, que ya existen.

02La calculadora, con el precio siempre a la vista

components/landing/calculator.tsx · box-input-row.tsx · reception-location-combobox.tsx · shipping-price.ts

/calculator es la misma <Calculator/> que usan la app web y la landing, con el mismo motor (@mogos/shared shipping-price.ts + shipping-regions.ts) y los mismos tramos (GET /shipping-pricing?corridorCode=). Por eso da el mismo número al centavo. No hay botón «Calcular»: el resultado se recalcula solo. En la web, las dos cards van lado a lado. En el teléfono el formulario va en una columna y la tarifa vive en una barra fija con el mogos-day-band que abre la card de la web. «Ver detalle» sube la card completa en una hoja.

  1. La barra es la cabeza de la card

    Tiene el mismo degradado celeste → lima y la misma cifra en display que el hero de «Estimación del envío», más una línea con método y destino. Se actualiza en vivo: cada cambio que da un resultado válido hace entrar el número nuevo desde abajo, en 260 ms.

  2. El formulario, campo a campo

    Ruta: CorridorTabs con bandera y el pie de unidades. Lugar de recepción: 24 estados, con Distrito Capital por defecto. Tipo de envío: tabs con subrayado de marca, Aéreo por defecto. Por caja: el modo (Dimensiones o Kg/Lb/CBM/Ft³ según la ruta y el modo), ancho, largo, alto, peso y número de cajas (1–10), con UnitBadge del color del corredor. Los valores al entrar son los de la web: 50×60×40, 25 kg, 2 cajas.

  3. Tres columnas, no una

    En móvil la web pone los cinco campos en una sola columna. Aquí van ancho, largo y alto en fila, y peso, cajas y la papelera debajo, así se ve la caja entera sin scroll. En 110 px no cabe «Ingresa el ancho», así que el placeholder pasa a 0. El label y el accessibilityLabel ya dicen qué va en cada campo.

  4. Teclado numérico y sin validar

    keyboardType="decimal-pad" con coma o punto según el idioma, normalizado antes del motor. Como en la web, no hay mensajes de error: si una entrada es inválida o incompleta, la barra muestra «Completa los campos del formulario para ver tu estimación».

  5. Eventos quote_* portados

    quote_started {entry_point} al montar. Desde Crear va calculator; desde un deep link con ?ref= se usa ese valor. quote_form_started_filling se dispara en el primer toque, quote_form_completed en los CTA y quote_form_abandoned al desmontar o cuando la app pasa a segundo plano (el pagehide de la web). Todo va con la platform de MF-00.

Ver detalle la card de la web, en una hoja
Carga liviana gana el peso volumétrico · Carabobo $14/kg
Lugar de recepción búsqueda sin acentos
Marítimo · CBM directo tramo 0.6–1.99 · Mérida
Cambio de ruta Desde USA · sistema americano
Estados de la barra tarifas cargando y error

Cargando: el skeleton reemplaza la cifra y el botón. Error: «No se pudieron cargar las tarifas.» + Reintentar. En la web, Reintentar recarga la página; aquí hace refetch.

Cuentas de las maquetas (tramos del seed, factor VOLUMETRIC_KG_PER_M3 ≈ 199.136): (a) 2 × 50×60×40 = 0.24 m³ → 47.8 kg volumétricos; se cobran los 50 kg reales × $12 = $600.00. (b) 3 × 60×50×40 = 0.36 m³ → 71.7 kg volumétricos contra 24 kg reales; 71.69 × $14 = $1,003.65. (c) Marítimo, 1.5 CBM en el tramo 0.6–1.99 de occidente × $1,010 = $1,515.00. La web pinta ${totalPrice} en crudo («$600»); la app formatea el mismo número con en-US y 2 decimales.

03El catálogo: rieles arriba, meses abajo

catalog/page.tsx · catalog-card.tsx · catalog/[id]/catalog-detail-content.tsx

La web tiene una sola página con cuatro bloques: el banner, Destacados (con el badge «Recomendados»), Catálogos recientes (últimos 30 días, máximo 6, en una card de marca) y Todos los catálogos, agrupados por mes del más nuevo al más viejo. No hay búsqueda, filtros ni categorías, porque el modelo no los tiene. Cada catálogo es un PDF o un enlace externo, con una miniatura y un tamaño opcional. El brief hablaba de archivos, galería y compartir, pero la web no tiene nada de eso, así que la app tampoco.

  1. Los mismos bloques, con otra forma

    En la web los bloques son grillas y en móvil caen a una columna. Destacados y Recientes repiten cards que también salen en Todos, así que una columna mostraría la misma card tres veces seguidas. Por eso aquí Destacados y Recientes son rieles horizontales de cards de 264 px, y Todos es un índice por mes de filas compactas (§ siguiente). Los datos, el orden y las reglas son los de la web.

  2. La card, verbatim

    Tiene la miniatura con el velo inferior, «Nuevo» (brand-500) si tiene 30 días o menos, las pastillas PDF/Link y el tamaño, el título y la descripción a 2 líneas, la fecha larga y «Ver detalles». Un cambio de forma: toda la card es tocable, no solo el botón, porque en la web el resto de la card no hace nada y en el teléfono es el área natural.

  3. El badge ámbar

    La web usa bg-amber-500 de Tailwind, fuera de los tokens. La app usa warning-500 del producto, del mismo tono. Queda anotado como deuda de la web.

  4. El banner es un asset

    CatalogBanner-1200w.webp viaja en el bundle con expo-image y el mismo encuadre (center 35%). En la maqueta, una composición del sistema ocupa su lugar para no meter fotos en la pieza.

  5. Una sola llamada

    GET /catalogs/active es público, sin paginar y ordenado por fecha, con staleTime estático. «Nuevo», Recientes y los meses se calculan en el cliente con date-fns en el idioma del usuario. No hay eventos: la web no manda ninguno.

Recientes y por mes la card de marca + el índice
Detalle · PDF Destacado · PDF · 12 MB
Detalle · enlace externo abre el navegador del sistema
El PDF el visor de MF-04, con compartir
Cargando la forma de la card
No encontrado id borrado o inválido

«Descargar PDF» abre el PdfViewer nativo de MF-04: descarga el archivo, lo pinta y comparte con la hoja del sistema, donde se guarda en Archivos. Es lo que en la web hace window.open. «Ver catálogo externo» usa expo-web-browser. En el detalle, la descripción aparece una sola vez: la web la repite en el encabezado y en la card. Vacío: «No hay catálogos disponibles en este momento.» Error: «Error al cargar los catálogos. Por favor, intenta de nuevo.» + Reintentar (refetch, no recarga).

04Sedes Mogos: todo lo que tu proveedor necesita, a un toque de copiar

locations/page.tsx · location-card.tsx · locations-country-section.tsx · warehouse-type-filter.tsx · shipping-mark-qr-notice.tsx

La web muestra las direcciones a las que tu proveedor manda la carga: por cada sede, dirección, teléfono, receptor y tu Shipping Mark ya resuelta con tu nombre, cada uno con su botón de copiar, y un «Copiar toda la información» que arma el bloque completo para pegarlo en un chat. Arriba va el aviso de la marca con QR. El brief pedía abrir en Maps, WhatsApp y horarios. La web no tiene nada de eso (ni el modelo: no hay horarios ni WhatsApp), así que queda fuera. El DoD pasa a ser copiar la dirección y toda la información de una sede.

  1. El corredor manda

    La página no tiene selector propio: lee el corredor global de MF-03 (el chip de la barra superior). Con un corredor elegido, la web muestra una lista plana. Aquí esa lista lleva igual el encabezado del país (bandera + «China» + «2 sedes»), porque en una pantalla de stack el chip del corredor no se ve y sin encabezado no sabrías por qué solo aparece China.

  2. Filtro Marítimo / Aéreo

    «Todos · Marítimo · Aéreo», filtrado en el cliente por warehouseType, fijo bajo el encabezado al hacer scroll. Solo aparece si hay sedes. Va con los StatusChips del stack compartido (ley #28) en lugar de las pastillas propias de la web: es el mismo control que en envíos, fletes y cotizaciones.

  3. La card, fila a fila

    Título (displayName o el nombre del almacén), «Activa» y el tipo. Luego Dirección, Teléfono (phoneCountry + número; sin número, «Consultar con Mogos» y sin botón de copiar), Receptor y Shipping Mark en mono. Cada botón de copiar mide 36 px con 44 de toque, y al copiar cambia a ✓ verde durante 2 s, con háptica selection.

  4. Tu marca, resuelta

    resolveShippingMarkTemplate de @mogos/shared cambia [NOMBRE DEL CLIENTE], teléfono y dirección por tu perfil. [CODIGO DEL CLIENTE] queda como «el código de tu envío», porque aquí no hay orden. Si falta el nombre, se usa el ejemplo ficticio con el badge «Ejemplo».

  5. El aviso QR va siempre

    Se ve también mientras carga y con error, como en la web. Le cabe en 3 líneas y «Ver ejemplo» sube la etiqueta real (ShippingMarkLabel) en una hoja (§ siguiente).

Todos los corredores acordeón por país de origen
Copiar ✓ en el botón + toast
Ver ejemplo la etiqueta con QR, en una hoja
Cargando el aviso QR ya está
Error con reintentar

«Copiar toda la información» copia el bloque de la web: Sede · Almacén · Dirección · Teléfono · Receptor + Shipping Mark. Usa expo-clipboard y el toast Sileo con la clave de la web, {{label}} copiado al portapapeles, que da «Dirección copiado» (concordancia rota en la web, anotada como deuda para arreglarla en las dos a la vez). Con «Todos», los grupos se ordenan por cantidad de sedes y cada encabezado queda fijo. Sin sedes: «No hay sedes disponibles» / «Las sedes de Mogos aparecerán aquí próximamente». GET shipping-locations?active=true&perPage=100[&corridorId] pide sesión. No hay eventos.

05Tutoriales, listos y sin puerta

tutorials/page.tsx · tutorials/[id]/tutorial-detail-content.tsx · video-player.tsx · landing/video-card.tsx

Fabrizio decidió diseñarlos y construirlos, pero sin entrada. La web los escondió de todos lados (el menú de ayuda en 26bf55375: «no hay tutoriales publicados todavía») y deja la ruta viva. La app hace lo mismo: /(app)/tutorials y [id] existen y se abren por deep link, pero no hay fila en Más ni menú de ayuda (ese menú llega en MF-13). El día que la web reponga la entrada, la app la repone con una línea. DECISIONS #12 queda corregida: «solo desde ayuda» pasa a «sin entrada, como la web».

  1. Academia, como la miga de la web

    El título largo de la web («Haz envíos sin complicaciones.») no cabe en un StackHeader de 24 px. Por eso el encabezado dice Academia, que es el breadcrumb de la web, y el título largo pasa a un lead de marca con la descripción debajo.

  2. Una columna, no un riel

    La web pone las cards en un riel horizontal de 242 px. Sin el bloque de sesiones, el riel sería lo único en pantalla, así que aquí va una columna de cards a lo ancho con la miniatura en 16:9. La card es la de la web: miniatura, duración en la esquina, título a 2 líneas y descripción. No hay categoría ni progreso, porque el modelo no los tiene.

  3. Fuera el calendario de sesiones 1:1

    En la web está roto: el mes está fijo en mayo de 2025 y tocar un día no hace nada (// TODO: Implement learning session booking). También salen «¿Aún tienes dudas?» y «¿Primera vez?», que solo tienen sentido con él. learning-sessions no se porta.

  4. 10 de golpe, sin borradores

    La web pide GET /tutorials sin parámetros: le llegan 10 (perPage por defecto), incluidos los DRAFT. La app pide status=PUBLISHED y pagina con InfiniteList*. Es una mejora con motivo y queda anotada como deuda de la web.

El detalle reproductor 16:9 + Más información
Sin video «Video no disponible»
Hoy lo que se ve mientras no haya contenido
Formato no soportado URL que no es YouTube, Vimeo ni archivo
El reproductor las tres fuentes de la web

YouTube con react-native-youtube-iframe (mismo regex de 11 caracteres, rel=0, sin autoplay). Vimeo en un WebView con player.vimeo.com/video/{id}. .mp4/.webm/.ogg con expo-video y la miniatura como póster. El póster con el botón blanco es el de la web. Al tocar, el iframe pinta sus propios controles y pantalla completa. «Ver video en nueva pestaña» no existe en un teléfono, así que pasa a «Abrir el video en el navegador», con clave i18n nueva. El detalle pinta content con el MarkdownViewer que ya usa Mogui. No hay eventos.

06Lo que hay que escribir en Moti

Moti + Reanimated · escala de MF-01 · useReducedMotion
PiezaQué animaDuración · easingReduced motion
Tarifa en la barrala cifra nueva entra desde 8 px abajo con fundido; con debounce de 120 ms al teclear para que no parpadee260 ms · ease-outswap directo
Hoja de estimaciónsube desde la barra (la barra queda debajo del velo); cierra con swipe o tocando el velo420 / 260 msfundido 80 ms
Agregar / quitar cajala caja nueva entra con fundido + 12 px; al quitarla se pliega260 ms · ease-in-outaparece / desaparece
Cambio de rutael UnitBadge cambia de color y texto con fundido; toast Sileo «Los valores se limpiaron…»160 · 640 springtoast directo
Rieles del catálogosnap por card (snapToInterval 276) con la inercia del sistemasistemasin cambios
Copiarel ícono pasa de copiar a ✓ con escala .8 → 1; vuelve a los 2 s; toast Sileo + háptica selection160 ms · springswap directo
Acordeón de paísel chevron gira 90°; el grupo se pliega con layout animation260 ms · ease-in-outdirecto
Cards · filasPressScale de MF-01 al tocar80 mssin escala

No hay un momento orquestado: son herramientas y el movimiento solo confirma. La barra de la calculadora es lo único que se mueve seguido, y por eso lleva debounce: el número cambia cuando dejas de teclear, no con cada dígito.

07Lo cerrado y lo que decide Fabrizio

docs/specs/2026-09-23-mobile-herramientas.md · HOLD hasta OK
  • Cerrada · Fabrizio 2026-09-23

    Tutoriales: se construyen, sin entrada

    Lista, detalle y reproductor listos, en las rutas /(app)/tutorials y [id], que se abren solo por deep link. No hay fila en Más ni en ayuda, como en la web desde 26bf55375. Fuera el calendario de sesiones 1:1, que en la web tiene el mes fijo y la reserva sin implementar. DECISIONS #12 se corrige.

  • Cerrada · Fabrizio 2026-09-23

    Sedes: paridad, sin mapas

    Ni «Abrir en Maps» ni WhatsApp ni horarios: la web no los tiene y el modelo tampoco. El DoD cambia a «copia la dirección y toda la información de una sede y pégala». Se quedan el filtro Marítimo/Aéreo, los grupos por país, copiar por campo, tu Shipping Mark resuelta y el aviso QR con ejemplo.

  • Cerrada · Fabrizio 2026-09-23

    Calculadora: barra fija + hoja

    El formulario va en una columna y la tarifa en vivo en una barra fija con el band de marca. «Ver detalle» sube la card «Estimación del envío» completa (filas, nota volumétrica, disclaimer, CBM) con «Solicitar cotización» y «Hacer envío». Mismo motor, mismos tramos, mismo número al centavo.

  • Cerrada · paridad

    El catálogo es PDF o enlace, nada más

    Sin galería, archivos múltiples ni compartir, porque el modelo tiene un solo url. El PDF abre en el PdfViewer de MF-04, que ya comparte y guarda. El enlace externo abre en expo-web-browser. Sin búsqueda ni categorías.

  • Cerrada · forma, con motivo

    Rieles e índice en el catálogo

    Destacados y Recientes van como rieles horizontales y Todos como índice por mes de filas compactas. En una columna, la misma card saldría hasta tres veces seguidas. La card entera es tocable.

  • Cerrada · reuso

    Lo que ya existe

    StackHeader (MF-04), StatusChips y InfiniteList* (MF-06, ley #28), PdfViewer (MF-04), toasts Sileo, PressScale y hojas (MF-01), el corredor global y corridorTheme (MF-03), MarkdownViewer (Mogui), y shipping-price.ts, shipping-regions.ts y resolveShippingMarkTemplate de @mogos/shared. Es nuevo: CatalogCard, LocationCard, ShippingMarkLabel nativo, VideoCard y VideoPlayer.

  • Corrección del brief

    Lo que el inventario desmintió

    No hay filtro por tipo de almacén en el sentido del brief (el que hay es Marítimo/Aéreo). No hay mapas, WhatsApp ni horarios. El catálogo no tiene archivos ni compartir. La calculadora no tiene CTA de registro: son «Solicitar cotización» y «Hacer envío». Tutoriales ya no está en la ayuda. OTHER_CREATE_ITEMS solo trae «Calcular costo» (el catálogo está en Más).

  • Deuda anotada

    Lo que la web debe y la app no repite

    El precio sin formato ($600). bg-amber-500 fuera de tokens. «{{label}} copiado» sin concordancia. Tutoriales sin filtro DRAFT y con 10 fijos. El calendario fijo en mayo de 2025. Los errores sin reintentar. La descripción del catálogo repetida. Las sugerencias de Mogui para el catálogo, que no corresponden («¿Cómo agrego productos a mi catálogo?»). El texto de discovery que aún nombra Tutoriales.

  • Testing

    DoD en iPhone = Fabrizio

    Calcular un envío aéreo y uno marítimo y comparar con la web al centavo. Abrir un catálogo PDF y uno externo. Ver las sedes de China y copiar toda la información de una. Abrir un tutorial por deep link y reproducirlo. C9 automática: expo export --platform web + visual-qa a 390×844 contra estas 25 maquetas (DECISIONS #4/#24). Los tramos se toman del seed de shipping-pricing-tiers.