12 KiB
12 KiB
FarmaFinder Mobile - Especificación de Diseño
Fecha: 2026-07-06 Estado: Aprobado Versión: 1.0
1. Resumen
Crear un segundo frontend en React Native (Expo) para FarmaFinder, manteniendo el frontend actual (React + Vite) para Desktop/PWA. El objetivo es lograr una experiencia móvil nativa que permita publicar en App Store y Google Play con mejor rendimiento, UX y mantenibilidad.
2. Arquitectura
2.1 Estructura del Proyecto
FarmaFinder/
├── frontend/ ← React + Vite (Desktop/PWA, mantiene Capacitor)
├── frontend-mobile/ ← Expo + React Native (NUEVO)
│ ├── app/ ← Expo Router (file-based routing)
│ │ ├── _layout.tsx ← Layout raíz
│ │ ├── (tabs)/ ← Bottom tabs
│ │ │ ├── _layout.tsx ← Tab navigator
│ │ │ ├── index.tsx ← Home (búsqueda)
│ │ │ ├── map.tsx ← Mapa de farmacias
│ │ │ └── profile.tsx ← Perfil de usuario
│ │ ├── medicine/[id].tsx ← Detalle de medicamento
│ │ ├── pharmacy/[id].tsx ← Detalle de farmacia
│ │ └── auth/ ← Auth flow
│ ├── components/ ← Componentes React Native
│ ├── services/ ← Lógica de negocio y llamadas API
│ ├── hooks/ ← Custom hooks
│ ├── constants/ ← Colores, tipos, config
│ └── assets/ ← Imágenes, fuentes
├── backend/ ← API compartida (sin cambios)
└── package.json ← Scripts para ambos frontends
2.2 Stack Tecnológico
| Componente | Tecnología | Justificación |
|---|---|---|
| Framework | Expo SDK 52+ | Setup rápido, OTA updates, fácil publicación |
| Routing | Expo Router v4 | File-based routing, similar a Next.js |
| Navegación | React Navigation | Bottom tabs + stack navigation |
| State | Zustand | Ligero, sin boilerplate, fácil de usar |
| HTTP Client | Axios | Interceptors, cancelación de requests |
| Cache | TanStack Query | Cache automático, optimistic updates |
| Mapas | react-native-maps | Google Maps provider nativo |
| Barcode | expo-camera | Escaneo de código de barras nativo |
| Storage | AsyncStorage + expo-secure-store | Datos offline + tokens seguros |
| Animaciones | React Native Reanimated 3 | Animaciones fluidas en 60fps |
3. Estructura de Navegación
3.1 Navegación Principal
Bottom Tabs
├── Home (🔍) → Búsqueda de medicamentos
├── Mapa (📍) → Mapa de farmacias
└── Perfil (👤) → Perfil de usuario + Admin
3.2 Navegación Detallada
Home Tab (Stack)
├── index.tsx ← Lista de resultados de búsqueda
└── medicine/[id].tsx ← Detalle de medicamento
Mapa Tab (Stack)
├── map.tsx ← Mapa con marcadores de farmacias
└── pharmacy/[id].tsx ← Detalle de farmacia
Perfil Tab (Stack)
├── profile.tsx ← Info de usuario + opciones
├── auth/login.tsx ← Login (si no autenticado)
├── auth/register.tsx ← Registro
└── admin/ ← Panel admin (solo si auth + admin)
3.3 Flujos de Navegación
- Auth Flow: Si no hay token → redirigir a login
- Admin Flow: Si usuario es admin → mostrar acceso a admin
- Deep Linking: URLs como
farmafinder://medicine/123abren pantalla específica
4. Funcionalidades
4.1 MVP (Paridad con Web)
| Funcionalidad | Descripción |
|---|---|
| Búsqueda | Búsqueda en tiempo real de medicamentos vía API CIMA |
| Resultados | Lista de resultados con nombre, principio activo |
| Detalle medicamento | Precio, stock, farmacias que lo tienen |
| Mapa farmacias | Marcadores en mapa con ubicación |
| Detalle farmacia | Dirección, teléfono, medicamentos disponibles |
| Auth | Login/registro de usuarios |
4.2 Extras Nativos (NUEVO)
| Funcionalidad | Descripción |
|---|---|
| Push Notifications | Alertas de disponibilidad de medicamentos favoritos |
| Escáner código barras | Escanear medicamentos directamente con cámara |
| Biometría | Face ID / Touch ID para login rápido |
| Deep Links | Compartir medicamentos/farmacias via URL |
| Offline Cache | Medicamentos favoritos accesibles sin conexión |
5. Integración con la API
5.1 Cliente API
// frontend-mobile/services/api.ts
import axios from 'axios';
const API_BASE = __DEV__
? 'http://localhost:3001/api'
: 'https://tu-dominio.com/api';
export const api = axios.create({
baseURL: API_BASE,
timeout: 10000,
headers: { 'Content-Type': 'application/json' }
});
// Request interceptor para auth token
api.interceptors.request.use((config) => {
const token = getToken();
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
5.2 Endpoints
Públicos:
GET /api/medicines/search?q=<query>- Búsqueda de medicamentosGET /api/medicines/:nregistro- Detalle de medicamentoGET /api/medicines/:nregistro/pharmacies- Farmacias con medicamentoGET /api/pharmacies- Todas las farmacias
Autenticados:
POST /api/auth/login- LoginPOST /api/auth/logout- LogoutGET /api/auth/check- Verificar auth
Admin:
- CRUD de farmacias
- Búsqueda de medicamentos para admin
- Vincular medicamentos a farmacias
5.3 Estrategia de Cache
// Con TanStack Query
const { data, isLoading } = useQuery({
queryKey: ['medicines', searchQuery],
queryFn: () => searchMedicines(searchQuery),
staleTime: 5 * 60 * 1000, // 5 minutos
cacheTime: 30 * 60 * 1000, // 30 minutos
});
Persistencia offline:
- AsyncStorage para datos no sensibles
- expo-secure-store para tokens y credenciales
- React Query persistor para cache de queries
6. Diseño Visual
6.1 Design Tokens
// frontend-mobile/constants/theme.ts
export const colors = {
// Primary
primary: '#007AFF',
primaryLight: '#4DA3FF',
// Semantic
success: '#34C759', // Disponible
danger: '#FF3B30', // Sin stock
warning: '#FF9500', // Stock bajo
// Neutral
background: '#F2F2F7',
card: '#FFFFFF',
border: '#E5E5EA',
// Text
text: '#1C1C1E',
textSecondary: '#8E8E93',
textInverse: '#FFFFFF',
};
export const spacing = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
};
export const borderRadius = {
sm: 8,
md: 12,
lg: 16,
};
6.2 Componentes Principales
| Componente | Descripción |
|---|---|
| SearchBar | Input con debounce (300ms) + dropdown de resultados |
| MedicineCard | Card con nombre, principio activo, precio, badge de stock |
| PharmacyMarker | Marcador personalizado en mapa con info window |
| StockBadge | Badge verde (disponible) / rojo (sin stock) |
| LoadingSpinner | Spinner nativo durante cargas |
| ErrorToast | Toast de error con retry |
6.3 Patrones de Diseño
- Cards: Sombra sutil, border-radius 12px
- Tipografía: Sistema nativo (SF Pro en iOS, Roboto en Android)
- Espaciado: Escala de 4px base (4, 8, 12, 16, 24, 32)
- Animaciones: Reanimated 3 para transiciones y gestos
7. Extras Nativos (Detalles)
7.1 Push Notifications
// Expo Notifications
import * as Notifications from 'expo-notifications';
// Registrar para notificaciones
const registerForPushNotifications = async () => {
const { status } = await Notifications.requestPermissionsAsync();
if (status !== 'granted') return;
const token = await Notifications.getExpoPushToken();
return token.data;
};
// Escuchar notificaciones
Notifications.addNotificationReceivedListener((notification) => {
// Mostrar alerta de disponibilidad
});
7.2 Escáner de Código de Barras
// expo-camera + barcode scanning
import { CameraView } from 'expo-camera';
<CameraView
onBarcodeScanned={(data) => {
// Buscar medicamento por código de barras
searchMedicineByBarcode(data.data);
}}
/>
7.3 Biometría
// expo-local-authentication
import * as LocalAuthentication from 'expo-local-authentication';
const authenticateWithBiometrics = async () => {
const hasHardware = await LocalAuthentication.hasHardwareAsync();
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
if (hasHardware && isEnrolled) {
return await LocalAuthentication.authenticateAsync();
}
return false;
};
8. Workflow de Desarrollo
8.1 Scripts
{
"scripts": {
"dev:mobile": "cd frontend-mobile && npx expo start",
"dev:mobile:android": "cd frontend-mobile && npx expo start --android",
"dev:mobile:ios": "cd frontend-mobile && npx expo start --ios",
"build:mobile:dev": "cd frontend-mobile && eas build --profile development",
"build:mobile:preview": "cd frontend-mobile && eas build --profile preview",
"build:mobile:prod": "cd frontend-mobile && eas build --profile production",
"submit:android": "cd frontend-mobile && eas submit --platform android",
"submit:ios": "cd frontend-mobile && eas submit --platform ios"
}
}
8.2 EAS Build Profiles
// frontend-mobile/eas.json
{
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal"
},
"production": {}
}
}
8.3 CI/CD
- Desarrollo: Expo Go + hot reload
- Testing: EAS Build (preview) → APK/IPA para testing
- Producción: EAS Build (production) → Submit a tiendas
- OTA Updates: Expo Updates para fixes sin pasar por tienda
9. Dependencias Principales
{
"dependencies": {
"expo": "~52.0.0",
"expo-router": "~4.0.0",
"expo-camera": "~16.0.0",
"expo-notifications": "~0.29.0",
"expo-local-authentication": "~15.0.0",
"expo-secure-store": "~14.0.0",
"react-native": "0.76.0",
"react-native-maps": "1.18.0",
"react-native-reanimated": "~3.16.0",
"react-native-gesture-handler": "~2.20.0",
"@react-navigation/native": "^7.0.0",
"@react-navigation/bottom-tabs": "^7.0.0",
"zustand": "^5.0.0",
"axios": "^1.7.0",
"@tanstack/react-query": "^5.0.0",
"@react-native-async-storage/async-storage": "^2.0.0"
}
}
10. Criterios de Aceptación
MVP
- Búsqueda de medicamentos funcional
- Mapa con farmacias mostrando marcadores
- Detalle de medicamento con precio y stock
- Detalle de farmacia con ubicación
- Login/registro funcional
- Navegación entre pantallas fluida
- Build de desarrollo funcionando en Expo Go
Extras Nativos
- Push notifications configuradas
- Escáner de código de barras funcional
- Biometría para login rápido
- Deep links configurados
- Offline cache para favoritos
Publicación
- Build de producción optimizado
- App Store listing configurado
- Google Play listing configurado
- Screenshots y descripción preparados
11. Riesgos y Mitigaciones
| Riesgo | Mitigación |
|---|---|
| API del backend no preparada para móvil | Revisar endpoints, agregar si es necesario |
| Performance en dispositivos antiguos | Testing en dispositivos低端, optimizar renders |
| Diferencias iOS/Android | Usar componentes nativos, testing en ambas plataformas |
| Mantenimiento de dos frontends | Documentar bien, compartir types cuando sea posible |
12. Próximos Pasos
- Crear estructura del proyecto Expo
- Configurar routing con Expo Router
- Implementar cliente API
- Crear pantallas MVP
- Implementar extras nativos
- Testing y optimización
- Build y publicación en tiendas