Mostrando entradas con la etiqueta lista filtrada. Mostrar todas las entradas
Mostrando entradas con la etiqueta lista filtrada. Mostrar todas las entradas

jueves, 24 de julio de 2025

Componente React de lista dinámica (filtrada por una condición)

Esto es código pleno. Consta de un array llamado dummySuggestions, al cual se le aplica un método filter para simular una búsqueda, cuyo resultado se guarda en un array llamado suggestions. Después, aplicando el método map al array suggestions, se generan elementos "ul" de JSX, que es lo que finalmente va a ser la lista de elementos filtrados. Sin más, acá va el código:

import React, { useState, useEffect, useCallback } from 'react';
import { Input } from '@/Components/ui/input'; // Componente Input de shadcn/ui
import { Button } from '@/Components/ui/button'; // Componente Button de shadcn/ui
import { MapPin, Search } from 'lucide-react'; // Iconos de lucide-react

// Este es un componente conceptual. En un proyecto real,
// integrarías una librería como @react-google-maps/api o similar
// para el autocompletado y geocodificación real.

function AddressInputWithMap({ initialData = {}, onAddressChange }) {
// Estado para los campos del domicilio
const [address, setAddress] = useState(initialData.address || '');
const [country, setCountry] = useState(initialData.country || '');
const [province, setProvince] = useState(initialData.province || '');
const [city, setCity] = useState(initialData.city || '');
const [district, setDistrict] = useState(initialData.district || ''); // Partido
const [locality, setLocality] = useState(initialData.locality || ''); // Localidad

// Estado para las sugerencias de autocompletado (simulado)
const [suggestions, setSuggestions] = useState([]);
const [loading, setLoading] = useState(false);

// Simula una llamada a una API de geocodificación/autocompletado
const fetchAddressSuggestions = useCallback(async (query) => {
if (query.length < 3) {
setSuggestions([]);
return;
}
setLoading(true);
// En un proyecto real, aquí harías un fetch a la API de Google Places Autocomplete
// o a tu propio backend que interactúe con una API de geocodificación.
// Por simplicidad, simulamos una respuesta.
const dummySuggestions = [
{
fullAddress: 'Av. Corrientes 1234, CABA, Argentina',
components: {
address: 'Av. Corrientes 1234',
city: 'Buenos Aires',
province: 'Ciudad Autónoma de Buenos Aires',
country: 'Argentina',
district: 'Comuna 3', // Ejemplo de partido/comuna
locality: 'Balvanera', // Ejemplo de localidad/barrio
},
},
{
fullAddress: 'Calle Falsa 123, Springfield, Buenos Aires, Argentina',
components: {
address: 'Calle Falsa 123',
city: 'Springfield',
province: 'Buenos Aires',
country: 'Argentina',
district: 'Partido de La Matanza', // Otro ejemplo
locality: 'Villa Elisa', // Otro ejemplo
},
},
{
fullAddress: 'Ruta 3 km 25, San Justo, Buenos Aires, Argentina',
components: {
address: 'Ruta 3 km 25',
city: 'San Justo',
province: 'Buenos Aires',
country: 'Argentina',
district: 'Partido de La Matanza',
locality: 'San Justo',
},
},
];

// Simular un retardo de red
await new Promise(resolve => setTimeout(resolve, 500));

const filteredSuggestions = dummySuggestions.filter(s =>
s.fullAddress.toLowerCase().includes(query.toLowerCase())
);
setSuggestions(filteredSuggestions);
setLoading(false);
}, []);

// Manejador para el cambio del input de dirección
const handleAddressInputChange = (e) => {
const value = e.target.value;
setAddress(value);
fetchAddressSuggestions(value);
};

// Manejador cuando se selecciona una sugerencia
const handleSelectSuggestion = (suggestion) => {
setAddress(suggestion.fullAddress);
setCountry(suggestion.components.country || '');
setProvince(suggestion.components.province || '');
setCity(suggestion.components.city || '');
setDistrict(suggestion.components.district || '');
setLocality(suggestion.components.locality || '');
setSuggestions([]); // Limpiar sugerencias
// Notificar al componente padre sobre el cambio de dirección completa
onAddressChange(suggestion.components);
};

// Efecto para notificar cambios al padre si los campos se editan manualmente
useEffect(() => {
onAddressChange({ address, country, province, city, district, locality });
}, [address, country, province, city, district, locality, onAddressChange]);


return (
<div className="space-y-4 p-4 border rounded-lg bg-white dark:bg-gray-800 shadow-sm">
<h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2">
<MapPin className="w-5 h-5 text-[#00A896]" />
Domicilio del Paciente
</h3>

{/* Campo de Dirección con Autocompletado */}
<div>
<label htmlFor="full-address" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Dirección Completa
</label>
<div className="relative">
<Input
id="full-address"
type="text"
value={address}
onChange={handleAddressInputChange}
placeholder="Ej: Av. Corrientes 1234"
className="w-full pr-10" // Espacio para el icono de búsqueda
/>
<Search className="absolute right-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
{loading && (
<div className="absolute top-full left-0 w-full bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-md shadow-lg mt-1 z-10 p-2 text-center text-sm text-gray-500 dark:text-gray-400">
Buscando...
</div>
)}
{suggestions.length > 0 && !loading && (
<ul className="absolute top-full left-0 w-full bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-md shadow-lg mt-1 z-10">
{suggestions.map((s, index) => (
<li
key={index}
className="px-3 py-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 text-sm"
onClick={() => handleSelectSuggestion(s)}
>
{s.fullAddress}
</li>
))}
</ul>
)}
</div>
</div>

{/* Campos rellenados automáticamente (o editables) */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label htmlFor="country" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
País
</label>
<Input
id="country"
type="text"
value={country}
onChange={(e) => setCountry(e.target.value)}
placeholder="País"
className="w-full"
/>
</div>
<div>
<label htmlFor="province" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Provincia
</label>
<Input
id="province"
type="text"
value={province}
onChange={(e) => setProvince(e.target.value)}
placeholder="Provincia"
className="w-full"
/>
</div>
<div>
<label htmlFor="city" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Ciudad
</label>
<Input
id="city"
type="text"
value={city}
onChange={(e) => setCity(e.target.value)}
placeholder="Ciudad"
className="w-full"
/>
</div>
<div>
<label htmlFor="district" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Partido
</label>
<Input
id="district"
type="text"
value={district}
onChange={(e) => setDistrict(e.target.value)}
placeholder="Partido (ej. Comuna, Condado)"
className="w-full"
/>
</div>
<div>
<label htmlFor="locality" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Localidad
</label>
<Input
id="locality"
type="text"
value={locality}
onChange={(e) => setLocality(e.target.value)}
placeholder="Localidad (ej. Barrio, Pueblo)"
className="w-full"
/>
</div>
</div>

{/* Un mapa visual (conceptual) */}
<div className="w-full aspect-video bg-gray-200 dark:bg-gray-700 rounded-md flex items-center justify-center text-gray-500 dark:text-gray-400 border border-gray-300 dark:border-gray-600">
{/* Aquí iría la integración real de un mapa (ej. Google Maps Embed) */}
<p>Mapa se mostraría aquí</p>
</div>

<Button className="w-full bg-blue-600 hover:bg-blue-700 text-white">
Guardar Domicilio
</Button>
</div>
);
}

// Ejemplo de cómo usarlo en un componente padre
function PatientForm() {
const [patientAddress, setPatientAddress] = useState({
address: 'Calle Inventada 456',
country: 'Argentina',
province: 'Córdoba',
city: 'Córdoba',
district: '',
locality: 'Nueva Córdoba'
});

const handleAddressUpdate = (newAddressComponents) => {
console.log('Dirección actualizada:', newAddressComponents);
setPatientAddress(newAddressComponents);
};

return (
<div className="p-8">
<h1 className="text-3xl font-bold text-center mb-8 text-gray-900 dark:text-white">
Formulario de Paciente
</h1>
<AddressInputWithMap initialData={patientAddress} onAddressChange={handleAddressUpdate} />
<div className="mt-6 p-4 border rounded-lg bg-gray-50 dark:bg-gray-900 text-gray-700 dark:text-gray-300 text-sm">
<h3 className="font-semibold mb-2">Datos del Domicilio guardados:</h3>
<pre>{JSON.stringify(patientAddress, null, 2)}</pre>
</div>
</div>
);
}

export default PatientForm;