Skip to content

Commit

Permalink
Merge pull request #69 from MartinGerritsen/feature/puntos-recogidas-map
Browse files Browse the repository at this point in the history
feat(Map) adding pickup points to map
  • Loading branch information
christiancp100 authored Nov 6, 2024
2 parents 771131d + 4d5f8a9 commit 1663341
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 137 deletions.
42 changes: 30 additions & 12 deletions src/app/casos-activos/mapa/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ import { useRouter, useSearchParams } from 'next/navigation';
import { tiposAyudaOptions } from '@/helpers/constants';
import Map from '@/components/map/map';
import ReactDOMServer from 'react-dom/server';
import { useTowns } from '../../../context/TownProvider';
import { useSession } from '../../../context/SessionProvider';
import PickupPoint from '@/components/PickupPoint';
import { useTowns } from '@/context/TownProvider';

const PAIPORTA_LAT_LNG = [-0.41667, 39.42333];
const DEFAULT_ZOOM = 12;

export default function Mapa() {
const towns = useTowns();
const session = useSession();
const searchParams = useSearchParams();
const router = useRouter();

Expand All @@ -34,6 +33,7 @@ export default function Mapa() {
urgencia: searchParams.get('urgencia') || 'todas',
tipoAyuda: searchParams.get('tipoAyuda') || 'todas',
pueblo: searchParams.get('pueblo') || 'todos',
acepta: searchParams.get('acepta') || 'todos',
});

const changeDataFilter = (type, newFilter) => {
Expand All @@ -56,36 +56,53 @@ export default function Mapa() {
};
}

// caso,
// towns,
// isHref,
// button = { text: 'Ver solicitud', link: '/solicitud/' },
// isEdit = false,
function transformPickupRequestToMarker(point) {
return {
urgency: point.urgency || 'baja',
coordinates: [point.longitude ?? 0, point.latitude ?? 0],
width: '600px',
descriptionHTML: ReactDOMServer.renderToString(<PickupPoint point={point} />),
};
}

async function fetchData() {
try {
setLoading(true);
setError(null);

// Comenzamos la consulta
const query = supabase.from('help_requests').select('*').eq('type', 'necesita');

if (filtroData.tipoAyuda !== 'todas') {
query.contains('help_type', [filtroData.tipoAyuda]);
}

if (filtroData.urgencia !== 'todas') {
query.eq('urgency', filtroData.urgencia);
}

const { data, error } = await query.order('created_at', { ascending: false });

const pickupQuery = supabase.from('collection_points').select('*', { count: 'exact' });
if (filtroData.acepta !== 'todos') {
query.contains('accepted_items', [filtroData.acepta]);
}

const { data: pickupData, error: pickupError } = await pickupQuery.order('created_at', { ascending: false });

let allData = [];
if (error) {
console.log('Error fetching solicitudes:', error);
setData([]);
} else {
const markers = data.map(transformHelpRequestToMarker);
setData(markers || []);
allData.push(...(markers || []));
}
if (pickupError) {
console.log('Error fetching pickup points:', pickupError);
} else {
const pickupMarkers = pickupData.map(transformPickupRequestToMarker);
allData.push(...(pickupMarkers || []));
}

setData(allData);
} catch (err) {
console.log('Error general:', err);
setError('Error de conexión.');
Expand Down Expand Up @@ -143,6 +160,7 @@ export default function Mapa() {
</select>
</div>
</div>

<div className="grid gap-4">
<Map markers={data} center={PAIPORTA_LAT_LNG} zoom={DEFAULT_ZOOM} />
</div>
Expand Down
75 changes: 3 additions & 72 deletions src/app/casos-activos/puntos/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { supabase } from '@/lib/supabase/client';
import { tiposAyudaAcepta } from '@/helpers/constants';
import Pagination from '@/components/Pagination';
import { useRouter, useSearchParams } from 'next/navigation';
import { useTowns } from '../../../context/TownProvider';
import PickupPoint from '@/components/PickupPoint';
import { useTowns } from '@/context/TownProvider';

export default function Puntos() {
const towns = useTowns();
Expand Down Expand Up @@ -138,77 +139,7 @@ export default function Puntos() {
</button>
</div>
) : (
data.map((punto) => (
<div key={punto.id} className="bg-white p-4 rounded-lg shadow-lg border-l-4 border-blue-500">
<div className="flex flex-col sm:flex-row justify-between items-start gap-2 mb-4">
<div>
<h3 className="text-lg font-bold text-blue-600 break-words">{punto.name}</h3>
<div className="flex items-start gap-2 text-gray-600 mt-1">
<MapPin className="h-4 w-4 flex-shrink-0 mt-1" />
<a
href={`https://maps.google.com/?q=${punto.location}`}
target="_blank"
rel="noopener noreferrer"
className="text-sm break-words text-blue-500 underline"
>
{punto.location}
</a>
</div>
</div>
<div>
<span className={`px-3 py-1 rounded-full text-sm font-medium whitespace-nowrap mr-2 bg-purple-300`}>
Referencia: {punto.id}
</span>
<span className="px-3 py-1 rounded-full bg-blue-100 text-blue-800 text-sm font-medium whitespace-nowrap">
{punto.status === 'active' ? 'Activo' : 'Inactivo'}
</span>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm mt-4">
<div className="break-words">
<span className="font-semibold">Ciudad:</span> {punto.city}
</div>
{punto.contact_name && (
<div className="break-words">
<span className="font-semibold">Responsable:</span> {punto.contact_name}
</div>
)}
{punto.contact_phone && (
<div className="break-words">
<span className="font-semibold">Teléfono:</span> {punto.contact_phone}
</div>
)}
{punto.accepted_items && (
<div className="col-span-1 sm:col-span-2 break-words">
<span className="font-semibold">Acepta:</span>{' '}
{Array.isArray(punto.accepted_items) ? punto.accepted_items.join(', ') : punto.accepted_items}
</div>
)}
{punto.urgent_needs && (
<div className="col-span-1 sm:col-span-2">
<span className="font-semibold">Necesidades urgentes:</span>
<p className="text-gray-700 mt-1 break-words">{punto.urgent_needs}</p>
</div>
)}
{punto.schedule && (
<div className="col-span-1 sm:col-span-2">
<span className="font-semibold">Horario:</span>
<p className="text-gray-700 mt-1 break-words">{punto.schedule}</p>
</div>
)}
{punto.additional_info && (
<div className="col-span-1 sm:col-span-2 bg-gray-50 p-3 rounded">
<span className="font-semibold">Información adicional:</span>
<p className="text-gray-700 mt-1 break-words">
{typeof punto.additional_info === 'string'
? punto.additional_info
: JSON.stringify(punto.additional_info)}
</p>
</div>
)}
</div>
</div>
))
data.map((punto) => <PickupPoint key={`pickup-point-${punto.id}`} point={punto} />)
)}
</div>
<div className="flex items-center justify-center">
Expand Down
77 changes: 77 additions & 0 deletions src/components/PickupPoint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { MapPin } from 'lucide-react';

const PickupPoint = ({ point }) => {
return (
<div className="bg-white p-4 rounded-lg shadow-lg border-l-4 border-blue-500">
<div className="flex flex-col sm:flex-row justify-between items-start gap-2 mb-4">
<div>
<h3 className="text-lg font-bold text-blue-600 break-words">{point.name}</h3>
<div className="flex items-start gap-2 text-gray-600 mt-1">
<MapPin className="h-4 w-4 flex-shrink-0 mt-1" />
<a
href={`https://maps.google.com/?q=${point.location}`}
target="_blank"
rel="noopener noreferrer"
className="text-sm break-words text-blue-500 underline"
>
{point.location}
</a>
</div>
</div>
<div>
<span className={`px-3 py-1 rounded-full text-sm font-medium whitespace-nowrap mr-2 bg-purple-300`}>
Referencia: {point.id}
</span>
<span className="px-3 py-1 rounded-full bg-blue-100 text-blue-800 text-sm font-medium whitespace-nowrap">
{point.status === 'active' ? 'Activo' : 'Inactivo'}
</span>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm mt-4">
<div className="break-words">
<span className="font-semibold">Ciudad:</span> {point.city}
</div>
{point.contact_name && (
<div className="break-words">
<span className="font-semibold">Responsable:</span> {point.contact_name}
</div>
)}
{point.contact_phone && (
<div className="break-words">
<span className="font-semibold">Teléfono:</span> {point.contact_phone}
</div>
)}
{point.accepted_items && (
<div className="col-span-1 sm:col-span-2 break-words">
<span className="font-semibold">Acepta:</span>{' '}
{Array.isArray(point.accepted_items) ? point.accepted_items.join(', ') : point.accepted_items}
</div>
)}
{point.urgent_needs && (
<div className="col-span-1 sm:col-span-2">
<span className="font-semibold">Necesidades urgentes:</span>
<p className="text-gray-700 mt-1 break-words">{point.urgent_needs}</p>
</div>
)}
{point.schedule && (
<div className="col-span-1 sm:col-span-2">
<span className="font-semibold">Horario:</span>
<p className="text-gray-700 mt-1 break-words">{point.schedule}</p>
</div>
)}
{point.additional_info && (
<div className="col-span-1 sm:col-span-2 bg-gray-50 p-3 rounded">
<span className="font-semibold">Información adicional:</span>
<p className="text-gray-700 mt-1 break-words">
{typeof point.additional_info === 'string'
? point.additional_info
: JSON.stringify(point.additional_info)}
</p>
</div>
)}
</div>
</div>
);
};

export default PickupPoint;
53 changes: 0 additions & 53 deletions src/components/map/map.js

This file was deleted.

74 changes: 74 additions & 0 deletions src/components/map/map.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { FC, useEffect, useRef } from 'react';
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';

const urgencyToColor = {
alta: '#ef4444', //text-red-500
media: '#f59e0b', //text-amber-500
baja: '#10b981', //text-emerald-500
};

type MapProps = {
center?: [number, number];
zoom?: number;
markers?: {
coordinates: [number, number];
urgency: 'alta' | 'media' | 'baja';
descriptionHTML: string;
width: number;
}[];
};

const Map: FC<MapProps> = ({ center = [0, 0], zoom = 2, markers = [] }) => {
const mapContainerRef = useRef<HTMLDivElement | null>(null);
const mapRef = useRef<maplibregl.Map | null>(null);
const markerRefs = useRef<maplibregl.Marker[]>([]);

useEffect(() => {
if (!mapRef.current) {
mapRef.current = new maplibregl.Map({
container: mapContainerRef.current!,
style: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
center: center,
zoom: zoom,
});
mapRef.current.addControl(new maplibregl.NavigationControl(), 'top-right');
} else {
// Update center and zoom when props change
mapRef.current.setCenter(center);
mapRef.current.setZoom(zoom);
}

// Clear existing markers
markerRefs.current.forEach((marker) => marker.remove());
markerRefs.current = [];

// Add new markers
markers.forEach((markerData) => {
const marker = new maplibregl.Marker({
color: urgencyToColor[markerData.urgency],
})
.setLngLat(markerData.coordinates)
.setPopup(
new maplibregl.Popup({
className: 'map-popup',
maxWidth: `${markerData.width}px`,
anchor: 'left',
}).setHTML(markerData.descriptionHTML)
)
.addTo(mapRef.current!);

markerRefs.current.push(marker);
});

// Clean up function to remove markers only
return () => {
markerRefs.current.forEach((marker) => marker.remove());
markerRefs.current = [];
};
}, [center, zoom, markers]);

return <div ref={mapContainerRef} style={{ width: '100%', height: '75vh' }} />;
};

export default Map;

0 comments on commit 1663341

Please sign in to comment.