feat: avatar popup options, profile redesign, and search dosage parsing
- Added avatar selection popup with predefined avatar options - Redesigned ProfileView (web + mobile) with avatar upload and editable fields - Added AvatarSelectionModal for mobile profile - Fixed medicine search: parse dosage terms (e.g. 'Paracetamol 1G') to query CIMA only by name and filter by dosage field - Updated styles for profile, search, and medicine results
This commit is contained in:
@@ -2,22 +2,56 @@ import axios from 'axios';
|
||||
import redisClient from './redis-client.js';
|
||||
|
||||
const CIMA_API_BASE_URL = 'https://cima.aemps.es/cima/rest';
|
||||
const CACHE_TTL = 3600; // 1 hora en segundos
|
||||
const CACHE_TTL = 3600;
|
||||
|
||||
/**
|
||||
* CIMA's nombre filter is prefix-oriented; narrow to rows that contain every
|
||||
* search term in the commercial name or active ingredient (full-word style).
|
||||
* Separa los términos de dosis (ej: "1g", "1000 mg") del nombre del
|
||||
* medicamento, para poder buscar en CIMA solo con el nombre y luego
|
||||
* filtrar por dosis con mayor precisión.
|
||||
*/
|
||||
function parseSearchQuery(query) {
|
||||
const terms = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
||||
|
||||
const dosagePattern = /^\d+(g|mg|mcg|µg|ml|ui|%)$/i;
|
||||
const dosageTerms = [];
|
||||
const nameTerms = [];
|
||||
|
||||
for (const term of terms) {
|
||||
const clean = term.replace(/\s+/g, '');
|
||||
if (dosagePattern.test(clean)) {
|
||||
dosageTerms.push(clean);
|
||||
} else {
|
||||
nameTerms.push(term);
|
||||
}
|
||||
}
|
||||
|
||||
return { nameQuery: nameTerms.join(' '), dosageTerms };
|
||||
}
|
||||
|
||||
/**
|
||||
* Filtra los resultados de CIMA comprobando que todos los términos del
|
||||
* nombre aparezcan en el nombre comercial o principio activo, y que los
|
||||
* términos de dosis coincidan con el campo dosage normalizado.
|
||||
*/
|
||||
function filterMedicinesByFullQuery(medicines, searchTerm) {
|
||||
const terms = searchTerm
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean);
|
||||
if (terms.length === 0) return medicines;
|
||||
const parsed = parseSearchQuery(searchTerm);
|
||||
const nameTerms = parsed.nameQuery.split(/\s+/).filter(Boolean);
|
||||
const dosageTerms = parsed.dosageTerms;
|
||||
|
||||
if (nameTerms.length === 0 && dosageTerms.length === 0) return medicines;
|
||||
|
||||
return medicines.filter((m) => {
|
||||
const hay = `${m.name || ''} ${m.active_ingredient || ''}`.toLowerCase();
|
||||
return terms.every((term) => hay.includes(term));
|
||||
if (nameTerms.length > 0) {
|
||||
const hay = `${m.name || ''} ${m.active_ingredient || ''}`.toLowerCase();
|
||||
if (!nameTerms.every((term) => hay.includes(term))) return false;
|
||||
}
|
||||
|
||||
if (dosageTerms.length > 0) {
|
||||
const dosageHay = (m.dosage || '').toLowerCase().replace(/\s+/g, '');
|
||||
if (!dosageTerms.every((term) => dosageHay.includes(term))) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -32,7 +66,7 @@ export async function searchMedicines(query) {
|
||||
}
|
||||
|
||||
const searchTerm = query.trim().toLowerCase();
|
||||
const cacheKey = `medicines:search:v2:${searchTerm}`;
|
||||
const cacheKey = `medicines:search:v3:${searchTerm}`;
|
||||
|
||||
try {
|
||||
// Intentar obtener del caché
|
||||
@@ -43,11 +77,13 @@ export async function searchMedicines(query) {
|
||||
return JSON.parse(cachedData);
|
||||
}
|
||||
|
||||
// Si no está en caché, consultar la API de CIMA
|
||||
console.log(`🌐 Fetching from CIMA API: ${searchTerm}`);
|
||||
const parsed = parseSearchQuery(searchTerm);
|
||||
const apiSearchTerm = parsed.nameQuery || searchTerm;
|
||||
|
||||
console.log(`🌐 Fetching from CIMA API: ${apiSearchTerm}`);
|
||||
const response = await axios.get(`${CIMA_API_BASE_URL}/medicamentos`, {
|
||||
params: {
|
||||
nombre: searchTerm
|
||||
nombre: apiSearchTerm
|
||||
},
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user