Frontend Hotfixes & Backend improvements
This commit is contained in:
@@ -151,19 +151,52 @@ export async function getMedicineDetails(nregistro) {
|
||||
return JSON.parse(cachedData);
|
||||
}
|
||||
|
||||
// Consultar la API de CIMA
|
||||
// Intentar obtener de la API de CIMA (endpoint individual puede no existir)
|
||||
console.log(`🌐 Fetching medicine details from CIMA: ${nregistro}`);
|
||||
const response = await axios.get(`${CIMA_API_BASE_URL}/medicamento/${nregistro}`, {
|
||||
try {
|
||||
const response = await axios.get(`${CIMA_API_BASE_URL}/medicamento/${nregistro}`, {
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
if (response.data) {
|
||||
const med = response.data;
|
||||
const medicineDetails = {
|
||||
id: med.nregistro,
|
||||
nregistro: med.nregistro,
|
||||
name: med.nombre,
|
||||
active_ingredient: med.principiosActivos?.[0]?.nombre || med.vtm?.nombre || null,
|
||||
dosage: med.dosis || null,
|
||||
form: med.formaFarmaceutica?.nombre || null,
|
||||
formSimplified: med.formaFarmaceuticaSimplificada?.nombre || null,
|
||||
laboratory: med.labtitular,
|
||||
prescription: med.cpresc,
|
||||
commercialized: med.comerc,
|
||||
generic: med.generico,
|
||||
photos: med.fotos || [],
|
||||
docs: med.docs || [],
|
||||
presentations: med.presentaciones || []
|
||||
};
|
||||
|
||||
await redisClient.setEx(cacheKey, CACHE_TTL * 24, JSON.stringify(medicineDetails));
|
||||
return medicineDetails;
|
||||
}
|
||||
} catch (apiError) {
|
||||
console.log(`⚠️ Individual medicine endpoint failed, trying search fallback`);
|
||||
}
|
||||
|
||||
// Fallback: buscar por nregistro usando el endpoint de búsqueda
|
||||
const searchResponse = await axios.get(`${CIMA_API_BASE_URL}/medicamentos`, {
|
||||
params: { nregistro },
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
if (response.data) {
|
||||
const med = response.data;
|
||||
if (searchResponse.data?.resultados?.length > 0) {
|
||||
const med = searchResponse.data.resultados[0];
|
||||
const medicineDetails = {
|
||||
id: med.nregistro,
|
||||
nregistro: med.nregistro,
|
||||
name: med.nombre,
|
||||
active_ingredient: med.principiosActivos?.[0]?.nombre || med.vtm?.nombre || null,
|
||||
active_ingredient: med.vtm?.nombre || null,
|
||||
dosage: med.dosis || null,
|
||||
form: med.formaFarmaceutica?.nombre || null,
|
||||
formSimplified: med.formaFarmaceuticaSimplificada?.nombre || null,
|
||||
@@ -172,13 +205,10 @@ export async function getMedicineDetails(nregistro) {
|
||||
commercialized: med.comerc,
|
||||
generic: med.generico,
|
||||
photos: med.fotos || [],
|
||||
docs: med.docs || [],
|
||||
presentations: med.presentaciones || []
|
||||
docs: med.docs || []
|
||||
};
|
||||
|
||||
// Guardar en caché (TTL más largo para detalles específicos)
|
||||
await redisClient.setEx(cacheKey, CACHE_TTL * 24, JSON.stringify(medicineDetails));
|
||||
|
||||
return medicineDetails;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import MapView, { Marker } from 'react-native-maps';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { getPharmacies } from '../../services/pharmacies';
|
||||
import { LoadingSpinner } from '../../components/LoadingSpinner';
|
||||
import { colors, spacing } from '../../constants/theme';
|
||||
import { colors, spacing, borderRadius } from '../../constants/theme';
|
||||
import { Pharmacy } from '../../types';
|
||||
|
||||
export default function MapScreen() {
|
||||
|
||||
@@ -97,10 +97,10 @@ const bgStyles = StyleSheet.create({
|
||||
flex: 1,
|
||||
},
|
||||
backgroundImage: {
|
||||
opacity: 0.5,
|
||||
opacity: 0.8,
|
||||
},
|
||||
overlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: 'rgba(255, 252, 245, 0.48)',
|
||||
backgroundColor: 'rgba(255, 252, 245, 0.2)',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -56,17 +56,18 @@ export default function MedicineDetailScreen() {
|
||||
return (
|
||||
<ScrollView style={styles.container}>
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.name}>{medicine.nombre}</Text>
|
||||
<StockBadge stock={medicine.stock} />
|
||||
<Text style={styles.name}>{medicine.name}</Text>
|
||||
{medicine.stock != null && <StockBadge stock={medicine.stock} />}
|
||||
</View>
|
||||
|
||||
<View style={styles.infoSection}>
|
||||
<InfoRow label="Principio activo" value={medicine.principioActivo} />
|
||||
<InfoRow label="Laboratorio" value={medicine.laboratorio} />
|
||||
<InfoRow label="Forma farmacéutica" value={medicine.formaFarmaceutica} />
|
||||
<InfoRow label="Principio activo" value={medicine.active_ingredient} />
|
||||
<InfoRow label="Laboratorio" value={medicine.laboratory} />
|
||||
<InfoRow label="Forma farmacéutica" value={medicine.form} />
|
||||
<InfoRow label="Dosificación" value={medicine.dosage} />
|
||||
<InfoRow
|
||||
label="Precio"
|
||||
value={medicine.precio ? `${medicine.precio.toFixed(2)} €` : 'No disponible'}
|
||||
value={medicine.precio != null ? `${medicine.precio.toFixed(2)} €` : 'No disponible'}
|
||||
/>
|
||||
<InfoRow label="Registro" value={medicine.nregistro} />
|
||||
</View>
|
||||
|
||||
@@ -29,27 +29,27 @@ export function MedicineCard({ medicine }: MedicineCardProps) {
|
||||
>
|
||||
<View style={styles.header}>
|
||||
<Text style={[styles.name, isTablet && styles.nameTablet]} numberOfLines={2}>
|
||||
{medicine.nombre}
|
||||
{medicine.name}
|
||||
</Text>
|
||||
<StockBadge stock={medicine.stock} />
|
||||
{medicine.stock != null && <StockBadge stock={medicine.stock} />}
|
||||
</View>
|
||||
|
||||
<Text style={styles.principioActivo} numberOfLines={1}>
|
||||
{medicine.principioActivo}
|
||||
{medicine.active_ingredient}{medicine.dosage ? ` - ${medicine.dosage}` : ''}
|
||||
</Text>
|
||||
|
||||
<View style={styles.footer}>
|
||||
<View style={styles.priceContainer}>
|
||||
<Ionicons name="pricetag" size={14} color={colors.textSecondary} />
|
||||
<Text style={[styles.price, isTablet && styles.priceTablet]}>
|
||||
{medicine.precio ? `${medicine.precio.toFixed(2)} €` : 'Sin precio'}
|
||||
{medicine.precio != null ? `${medicine.precio.toFixed(2)} €` : 'Sin precio'}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.labContainer}>
|
||||
<Ionicons name="business" size={14} color={colors.textSecondary} />
|
||||
<Text style={[styles.laboratorio, isTablet && styles.laboratorioTablet]} numberOfLines={1}>
|
||||
{medicine.laboratorio}
|
||||
{medicine.laboratory}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -11,8 +11,10 @@ const ENV = {
|
||||
|
||||
const environment = Constants.expoConfig?.extra?.environment || (__DEV__ ? 'development' : 'production');
|
||||
|
||||
const envBaseUrl = process.env.EXPO_PUBLIC_API_URL;
|
||||
|
||||
export const config = {
|
||||
API_BASE_URL: ENV[environment as keyof typeof ENV]?.API_BASE_URL || ENV.development.API_BASE_URL,
|
||||
API_BASE_URL: envBaseUrl || ENV[environment as keyof typeof ENV]?.API_BASE_URL || ENV.development.API_BASE_URL,
|
||||
SEARCH_DEBOUNCE_MS: 300,
|
||||
CACHE_STALE_TIME: 5 * 60 * 1000, // 5 minutes
|
||||
CACHE_CACHE_TIME: 30 * 60 * 1000, // 30 minutes
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
export interface Medicine {
|
||||
id: string;
|
||||
nregistro: string;
|
||||
nombre: string;
|
||||
principioActivo: string;
|
||||
laboratorio: string;
|
||||
formaFarmaceutica: string;
|
||||
precio: number | null;
|
||||
stock: number;
|
||||
disponibilidad: 'disponible' | 'sin_stock' | 'bajo_stock';
|
||||
name: string;
|
||||
active_ingredient: string;
|
||||
dosage: string;
|
||||
form: string;
|
||||
formSimplified: string;
|
||||
laboratory: string;
|
||||
prescription: string;
|
||||
commercialized: boolean;
|
||||
generic: boolean;
|
||||
photos: string[];
|
||||
docs: { tipo: number; url: string; urlHtml?: string; secc: boolean; fecha: number }[];
|
||||
// Optional pharmacy-specific fields (only present in pharmacy_medicines joins)
|
||||
precio?: number | null;
|
||||
stock?: number;
|
||||
}
|
||||
|
||||
export interface Pharmacy {
|
||||
|
||||
Reference in New Issue
Block a user