- Campo universal de ubicación (Google Maps/Waze/OSM/coordenadas/dirección) sobre proveedores extensibles (src/utils/ubicacion) con resolver e inferencia inversa Nominatim (nombre/provincia pre-rellenados, alias cooficiales) - FAB flotante abajo a la derecha que abre la pasarela en un modal accesible (Escape, foco contenido, responsive); sin descripción ni puntuación; avisos de duplicado y filtro de palabras dentro del modal - Aviso PWA reposicionado: tarjeta abajo a la izquierda en móvil, botón compacto arriba a la izquierda en PC (no tapa el FAB) - Suite de pruebas: 88 unitarias/componente (vitest + RTL, offline) y 10 de integración (supertest + SQLite :memory: y Nominatim real opt-in) - CI: workflow de Gitea Actions con npm test + build en push/PR e integración manual
120 lines
6.2 KiB
React
120 lines
6.2 KiB
React
import { CATEGORIAS } from "../constants/categorias.js";
|
|
import { ETIQUETAS_PROVEEDOR } from "../utils/ubicacion/index.js";
|
|
|
|
const fila = { display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 10, padding: "6px 0", borderBottom: "0.5px solid var(--color-border-tertiary)", fontSize: 13 };
|
|
const clave = { color: "var(--color-text-secondary)", flexShrink: 0 };
|
|
const valor = { textAlign: "right", fontWeight: 500, wordBreak: "break-word" };
|
|
|
|
// Paso 6 (final) de la pasarela: resumen legible de todo lo introducido,
|
|
// aceptación obligatoria de la política de privacidad y envío de la
|
|
// propuesta. Los avisos de palabra prohibida y de posible duplicado se
|
|
// muestran dentro del propio modal.
|
|
export default function PasoResumen({
|
|
form, aceptaPrivacidad, setAceptaPrivacidad, onAbrirPrivacidad,
|
|
onEnviar, onConfirmarDuplicado, onDescartarDuplicado,
|
|
errorFiltro, posibleDuplicado, comprobandoDuplicado, saving,
|
|
}) {
|
|
const categoria = form.categoria ? CATEGORIAS[form.categoria] : null;
|
|
const enviando = comprobandoDuplicado || saving;
|
|
const etiquetaProveedor = form.proveedorUbicacion ? ETIQUETAS_PROVEEDOR[form.proveedorUbicacion] || form.proveedorUbicacion : "";
|
|
|
|
return (
|
|
<div>
|
|
<p style={{ margin: "0 0 8px", fontSize: 13, color: "var(--color-text-secondary)" }}>
|
|
Comprueba los datos antes de enviar. El administrador revisará la propuesta antes de publicarla.
|
|
</p>
|
|
|
|
<div style={{ background: "var(--color-background-secondary)", borderRadius: "var(--border-radius-md)", padding: "4px 12px", marginBottom: 12 }}>
|
|
<div style={fila}>
|
|
<span style={clave}>📍 Ubicación</span>
|
|
<span style={valor}>
|
|
{etiquetaProveedor && <>{etiquetaProveedor}<br /></>}
|
|
{form.lat != null && form.lng != null && <span style={{ fontWeight: 400 }}>{form.lat.toFixed(4)}, {form.lng.toFixed(4)}</span>}
|
|
{form.direccion && <span style={{ fontWeight: 400, display: "block" }}>{form.direccion}</span>}
|
|
</span>
|
|
</div>
|
|
<div style={fila}>
|
|
<span style={clave}>🏷️ Nombre</span>
|
|
<span style={valor}>{form.nombre}</span>
|
|
</div>
|
|
<div style={fila}>
|
|
<span style={clave}>🗺️ Provincia</span>
|
|
<span style={valor}>{form.provincia}</span>
|
|
</div>
|
|
<div style={fila}>
|
|
<span style={clave}>📂 Categoría</span>
|
|
<span style={valor}>{categoria ? `${categoria.emoji} ${form.categoria}` : ""}</span>
|
|
</div>
|
|
<div style={{ ...fila, borderBottom: "none" }}>
|
|
<span style={clave}>🔖 Tipo específico</span>
|
|
<span style={{ ...valor, fontWeight: form.subcategoria ? 500 : 400, color: form.subcategoria ? "inherit" : "var(--color-text-tertiary)" }}>
|
|
{form.subcategoria || "—"}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<label style={{ display: "flex", alignItems: "flex-start", gap: 8, marginBottom: 12, fontSize: 12, color: "var(--color-text-secondary)", cursor: "pointer" }}>
|
|
<input
|
|
type="checkbox"
|
|
checked={aceptaPrivacidad}
|
|
onChange={(e) => setAceptaPrivacidad(e.target.checked)}
|
|
style={{ marginTop: 2 }}
|
|
/>
|
|
<span>
|
|
He leído y acepto la{" "}
|
|
<button
|
|
type="button"
|
|
onClick={(e) => { e.preventDefault(); onAbrirPrivacidad(); }}
|
|
style={{ background: "none", border: "none", padding: 0, color: "#8B0000", textDecoration: "underline", cursor: "pointer", fontSize: 12 }}
|
|
>
|
|
política de privacidad
|
|
</button>
|
|
{" "}sobre cómo se usan estos datos. <span style={{ color: "#8B0000" }}>*</span>
|
|
</span>
|
|
</label>
|
|
|
|
{/* Aviso de palabra prohibida */}
|
|
{errorFiltro && (
|
|
<div role="alert" style={{ marginBottom: 12, background: "#FDEDEC", border: "1px solid #F5B7B1", borderRadius: "var(--border-radius-md)", padding: "10px 14px", fontSize: 13, color: "#922B21", display: "flex", alignItems: "center", gap: 8 }}>
|
|
<i className="ti ti-ban" aria-hidden="true"></i> {errorFiltro}
|
|
</div>
|
|
)}
|
|
|
|
{/* Aviso de posible duplicado */}
|
|
{posibleDuplicado && (
|
|
<div role="alert" style={{ marginBottom: 12, background: "#FDF2E9", border: "1px solid #F5CBA7", borderRadius: "var(--border-radius-md)", padding: "10px 14px", fontSize: 13, color: "#784212" }}>
|
|
<div style={{ display: "flex", alignItems: "center", gap: 8, fontWeight: 500 }}>
|
|
<i className="ti ti-alert-triangle" aria-hidden="true"></i> Ya existe un local parecido
|
|
</div>
|
|
<ul style={{ margin: "6px 0", paddingLeft: 20 }}>
|
|
{posibleDuplicado.coincidencias.map((c) => (
|
|
<li key={c.id}>{c.nombre} · {c.provincia}{c.direccion ? ` · ${c.direccion}` : ""} ({c.estado === "pendiente" ? "pendiente de revisión" : "ya publicado"})</li>
|
|
))}
|
|
</ul>
|
|
<div style={{ display: "flex", gap: 8, marginTop: 4 }}>
|
|
<button onClick={onConfirmarDuplicado} disabled={saving}
|
|
style={{ background: "#8B0000", color: "white", border: "none", borderRadius: "var(--border-radius-md)", padding: "6px 14px", fontSize: 12, fontWeight: 500, cursor: "pointer" }}>
|
|
{saving ? "Enviando..." : "Enviar de todos modos"}
|
|
</button>
|
|
<button onClick={onDescartarDuplicado} style={{ background: "none", border: "0.5px solid var(--color-border-secondary)", borderRadius: "var(--border-radius-md)", padding: "6px 14px", fontSize: 12, cursor: "pointer", color: "var(--color-text-secondary)" }}>Revisar datos</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<button
|
|
onClick={onEnviar}
|
|
disabled={!aceptaPrivacidad || enviando}
|
|
style={{
|
|
background: aceptaPrivacidad ? "#8B0000" : "var(--color-background-secondary)",
|
|
color: aceptaPrivacidad ? "white" : "var(--color-text-tertiary)",
|
|
border: "none", borderRadius: "var(--border-radius-md)",
|
|
padding: "10px 20px", fontSize: 14, fontWeight: 500, width: "100%",
|
|
cursor: aceptaPrivacidad && !enviando ? "pointer" : "not-allowed",
|
|
}}
|
|
>
|
|
{comprobandoDuplicado ? "Comprobando..." : saving ? "Enviando..." : "📨 Enviar propuesta"}
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|