diff --git a/apps/frontend/src/components/PharmacyMap.jsx b/apps/frontend/src/components/PharmacyMap.jsx
index ca9876c..7182157 100644
--- a/apps/frontend/src/components/PharmacyMap.jsx
+++ b/apps/frontend/src/components/PharmacyMap.jsx
@@ -34,6 +34,15 @@ function PharmacyMap({ pharmacies }) {
{pharmacy.name}
{pharmacy.address}
{pharmacy.phone && <>
{pharmacy.phone}>}
+
+
+ 📍 Cómo llegar
+
))}
diff --git a/apps/frontend/src/views/ProductView.jsx b/apps/frontend/src/views/ProductView.jsx
index 344533d..c6493ad 100644
--- a/apps/frontend/src/views/ProductView.jsx
+++ b/apps/frontend/src/views/ProductView.jsx
@@ -1,5 +1,7 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useMemo } from 'react';
import PharmacyMap from '../components/PharmacyMap';
+import PharmacyList from '../components/PharmacyList';
+import { haversineKm, getUserPosition, hasCachedPosition } from '../utils/geo';
import './ProductView.css';
export default function ProductView({ source, id, onBack }) {
@@ -8,6 +10,50 @@ export default function ProductView({ source, id, onBack }) {
const [error, setError] = useState(null);
const [pharmacies, setPharmacies] = useState([]);
const [loadingPharmacies, setLoadingPharmacies] = useState(false);
+ const [sortByDistance, setSortByDistance] = useState(false);
+ const [userPosition, setUserPosition] = useState(null);
+ const [positionSource, setPositionSource] = useState(null);
+ const [locating, setLocating] = useState(false);
+ const [locationError, setLocationError] = useState('');
+
+ const hasSavedCoords = product?.latitude != null && product?.longitude != null;
+
+ const displayedPharmacies = useMemo(() => {
+ if (!sortByDistance || !userPosition) return pharmacies;
+ return [...pharmacies].sort((a, b) => {
+ if (a.latitude == null || a.longitude == null) return 1;
+ if (b.latitude == null || b.longitude == null) return -1;
+ return (
+ haversineKm(userPosition.lat, userPosition.lon, a.latitude, a.longitude) -
+ haversineKm(userPosition.lat, userPosition.lon, b.latitude, b.longitude)
+ );
+ });
+ }, [pharmacies, sortByDistance, userPosition]);
+
+ const handleSortByDistance = async () => {
+ if (sortByDistance) {
+ setSortByDistance(false);
+ return;
+ }
+ setLocationError('');
+ setLocating(true);
+ try {
+ const pos = await getUserPosition();
+ setUserPosition(pos);
+ setPositionSource('browser');
+ setSortByDistance(true);
+ } catch (err) {
+ let msg = 'No se pudo obtener tu ubicación';
+ if (err && typeof err.code === 'number') {
+ if (err.code === 1) msg = 'Permiso de ubicación denegado.';
+ else if (err.code === 2) msg = 'Ubicación no disponible.';
+ else if (err.code === 3) msg = 'La ubicación tardó demasiado.';
+ }
+ setLocationError(msg);
+ } finally {
+ setLocating(false);
+ }
+ };
useEffect(() => {
loadProduct();
@@ -152,31 +198,38 @@ export default function ProductView({ source, id, onBack }) {
Disponible en {pharmacies.length} {pharmacies.length === 1 ? 'farmacia' : 'farmacias'}
-
{pharmacy.address}
- {pharmacy.phone && ( -{pharmacy.phone}
- )} -