feat(ui): notificaciones toast y mapa que sobrevive al cambio de pestaña
deploy-branch / deploy (push) Successful in 2m41s
deploy-branch / teardown (push) Skipped

- Añade sistema de avisos tipo snackbar (Toast.jsx + useToast.js) con
  auto-cierre, animación de entrada y estilos info/éxito/error.
- LocalCard: feedback visual al añadir/quitar un local de favoritos.
- AdminPanel: sustituye los alert() por toasts al aprobar, rechazar,
  eliminar locales, descartar avisos y guardar el filtro de palabras;
  además saluda con un toast tras iniciar sesión o crear cuenta.
- App: el contenedor del mapa Leaflet queda siempre montado (oculto con
  CSS) y se llama a invalidateSize() al volver a la pestaña de mapa;
  así el mapa ya no se rompe al navegar entre vistas (antes obligaba
  a recargar la página).
This commit is contained in:
2026-08-15 20:37:04 +02:00
parent b36867225c
commit d48b74cbd4
5 changed files with 109 additions and 29 deletions
+12 -1
View File
@@ -2,7 +2,9 @@ import { useState } from "react";
import CategoriaBadge from "./CategoriaBadge.jsx";
import StarRating from "./StarRating.jsx";
import ReportarLocal from "./ReportarLocal.jsx";
import Toast from "./Toast.jsx";
import useFavoritos from "../hooks/useFavoritos.js";
import useToast from "../hooks/useToast.js";
import { confirmarLocal } from "../firebase.js";
import { enlaceGoogleMapsDesdeLocal, enlaceWazeDesdeLocal, formatearDistancia } from "../utils/geo.js";
@@ -24,6 +26,7 @@ function mesesDesde(fecha) {
export default function LocalCard({ local, distanciaKm }) {
const { esFavorito, alternarFavorito } = useFavoritos();
const { toast, mostrarToast } = useToast();
const [confirmando, setConfirmando] = useState(false);
const [confirmado, setConfirmado] = useState(false);
const [fechaConfirmacion, setFechaConfirmacion] = useState(local.fechaConfirmacion || local.fecha);
@@ -47,6 +50,12 @@ export default function LocalCard({ local, distanciaKm }) {
setConfirmando(false);
};
const handleAlternarFavorito = () => {
const nuevoEstado = !favorito;
alternarFavorito(local.id);
mostrarToast(nuevoEstado ? "❤️ Añadido a favoritos" : "Quitado de favoritos", nuevoEstado ? "exito" : "info");
};
const compartir = async () => {
const url = `${window.location.origin}${window.location.pathname}?local=${local.id}`;
const texto = `${local.nombre}${local.provincia ? ` · ${local.provincia}` : ""}`;
@@ -82,7 +91,7 @@ export default function LocalCard({ local, distanciaKm }) {
style={{ background: "none", border: "none", cursor: "pointer", fontSize: 16, color: "var(--color-text-tertiary)", padding: 4 }}>
<i className="ti ti-share-2" aria-hidden="true"></i>
</button>
<button type="button" onClick={() => alternarFavorito(local.id)} title={favorito ? "Quitar de favoritos" : "Guardar en favoritos"}
<button type="button" onClick={handleAlternarFavorito} title={favorito ? "Quitar de favoritos" : "Guardar en favoritos"}
aria-pressed={favorito}
style={{ background: "none", border: "none", cursor: "pointer", fontSize: 16, color: favorito ? "#8B0000" : "var(--color-text-tertiary)", padding: 4 }}>
<i className={favorito ? "ti ti-heart-filled" : "ti ti-heart"} aria-hidden="true"></i>
@@ -152,6 +161,8 @@ export default function LocalCard({ local, distanciaKm }) {
)}
<ReportarLocal localId={local.id} />
</div>
<Toast toast={toast} />
</div>
);
}