refactor(ui): modulariza App.jsx y unifica branding y caché PWA
deploy-branch / deploy (push) Successful in 2m36s
deploy-branch / teardown (push) Skipped

- Extrae App.jsx en módulos reutilizables: constants/ (categorias,
  provincias), utils/geo, styles/shared, hooks/usePwaInstall y
  components/ (StarRating, LocalCard, CampoUbicacion, BannerMovil,
  CategoriaBadge)
- AdminPanel reutiliza constantes y componentes compartidos y corrige
  la limpieza de suscripciones de Firebase al desmontar
- Añade logos de marca (logo-localesp.png + @2x) y regenera los iconos
  PWA con tamaños menores
- sw.js: sube la caché del shell a v2 e incluye los nuevos iconos y logos
This commit is contained in:
2026-08-05 19:07:00 +02:00
parent 28d066b5af
commit 257fb9c622
20 changed files with 674 additions and 360 deletions
+58
View File
@@ -0,0 +1,58 @@
import CategoriaBadge from "./CategoriaBadge.jsx";
import StarRating from "./StarRating.jsx";
function formatearFecha(fecha) {
if (!fecha) return null;
const date = new Date(fecha);
if (Number.isNaN(date.getTime())) return null;
return date.toLocaleDateString("es-ES", { day: "numeric", month: "long", year: "numeric" });
}
export default function LocalCard({ local }) {
const gmUrl = local.enlaceGoogleMaps || (local.direccion
? `https://www.google.com/maps/search/${encodeURIComponent(`${local.nombre} ${local.direccion}`)}`
: null);
const fechaFormateada = formatearFecha(local.fecha);
return (
<div
style={{ background: "var(--color-background-primary)", border: "0.5px solid var(--color-border-tertiary)", borderRadius: "var(--border-radius-lg)", padding: "1rem 1.25rem", display: "flex", flexDirection: "column", gap: 8, transition: "border-color 0.2s" }}
onMouseEnter={(e) => { e.currentTarget.style.borderColor = "var(--color-border-secondary)"; }}
onMouseLeave={(e) => { e.currentTarget.style.borderColor = "var(--color-border-tertiary)"; }}
>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
<div style={{ flex: 1, minWidth: 0 }}>
<p style={{ fontWeight: 500, fontSize: 16, margin: 0 }}>{local.nombre}</p>
{local.provincia && <p style={{ fontSize: 13, color: "var(--color-text-secondary)", margin: "2px 0 0" }}>{local.provincia}</p>}
</div>
</div>
<CategoriaBadge categoria={local.categoria} subcategoria={local.subcategoria} />
{local.direccion && (
<div style={{ display: "flex", alignItems: "flex-start", gap: 6 }}>
<i className="ti ti-map-pin" aria-hidden="true" style={{ fontSize: 14, color: "var(--color-text-tertiary)", marginTop: 1, flexShrink: 0 }}></i>
<span style={{ fontSize: 13, color: "var(--color-text-secondary)" }}>{local.direccion}</span>
</div>
)}
{gmUrl && (
<a href={gmUrl} target="_blank" rel="noopener noreferrer"
style={{ display: "inline-flex", alignItems: "center", gap: 5, fontSize: 12, color: "#1A73E8", textDecoration: "none", width: "fit-content", background: "#E8F0FE", padding: "4px 10px", borderRadius: "var(--border-radius-md)", fontWeight: 500 }}>
<i className="ti ti-brand-google-maps" aria-hidden="true" style={{ fontSize: 14 }}></i>
Ver en Google Maps
</a>
)}
<StarRating value={local.puntuacion} readOnly />
{local.descripcion && (
<p style={{ fontSize: 13, color: "var(--color-text-secondary)", margin: 0, fontStyle: "italic" }}>"{local.descripcion}"</p>
)}
{fechaFormateada && (
<p style={{ fontSize: 11, color: "var(--color-text-tertiary)", margin: 0 }}>Publicado {fechaFormateada}</p>
)}
</div>
);
}