Modulo nuevo (solo admin) para dar de alta desarrollos sin tocar codigo: - CRUD de la ficha, modelos e inventario (alta manual y generador por rango) - Reglas del cotizador por desarrollo, editables y persistidas en la BD - El cotizador consume las reglas desde /desarrollos/reglas-cotizador y cae a las constantes con aviso cuando un desarrollo aun no tiene reglas guardadas - Micrositio publico en /d/:clave (sin sesion) con ficha, modelos y disponibilidad; jamas expone datos de clientes ni cotizaciones Integracion: - La landing monta el modulo en el grid (clave 'desarrollos', solo admin) y el micrositio fuera del login; el rol manager NO accede (matriz y API con 403) - La tabla de roles de Configuracion ahora lista todos los modulos - Se alinea la FK de ReglaCotizadorDesarrollo con la BD viva (Desarrollo.id) Verificado: 708 pruebas del API, 115 del cotizador y 89 de la landing en verde; 12/12 cotizaciones identicas antes y despues del cambio de reglas; prueba en vivo del API con alta, modelos, inventario, reglas, micrositio y baja (30/30).
70 lines
2.6 KiB
TypeScript
70 lines
2.6 KiB
TypeScript
import { Check, Sparkles } from 'lucide-react'
|
|
import { separarAmenidades } from './formato'
|
|
|
|
interface Props {
|
|
descripcion?: string | null
|
|
amenidades?: string | null
|
|
}
|
|
|
|
/** Descripción del desarrollo a dos columnas + amenidades como chips con palomita */
|
|
export function SeccionDescripcion({ descripcion, amenidades }: Props) {
|
|
const parrafos = (descripcion ?? '')
|
|
.split(/\n+/)
|
|
.map((p) => p.trim())
|
|
.filter(Boolean)
|
|
const listaAmenidades = separarAmenidades(amenidades)
|
|
|
|
if (parrafos.length === 0 && listaAmenidades.length === 0) return null
|
|
|
|
return (
|
|
<section
|
|
aria-labelledby="titulo-descripcion"
|
|
className="mx-auto w-full max-w-6xl px-4 py-16 sm:px-6 sm:py-20 lg:px-8"
|
|
>
|
|
<div className="grid gap-10 lg:grid-cols-2 lg:gap-14">
|
|
<div>
|
|
<p className="text-xs font-semibold tracking-[0.18em] text-accent uppercase">El desarrollo</p>
|
|
<h2
|
|
id="titulo-descripcion"
|
|
className="mt-3 text-2xl font-semibold tracking-tight text-[#111827] sm:text-3xl dark:text-white"
|
|
>
|
|
Sobre el desarrollo
|
|
</h2>
|
|
<div className="mt-5 space-y-4 text-base leading-relaxed text-[#4B5563] dark:text-[#CBD5E1]">
|
|
{parrafos.length > 0 ? (
|
|
parrafos.map((parrafo, indice) => <p key={indice}>{parrafo}</p>)
|
|
) : (
|
|
<p>
|
|
Escríbenos para conocer a detalle las características, acabados y formas de pago de
|
|
este desarrollo.
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{listaAmenidades.length > 0 ? (
|
|
<div className="rounded-2xl border border-[#E5E7EB] bg-white p-6 shadow-sm sm:p-8 dark:border-[#1E293B] dark:bg-[#1E293B]">
|
|
<h3 className="flex items-center gap-2 text-base font-semibold text-[#111827] dark:text-white">
|
|
<Sparkles aria-hidden="true" className="h-4 w-4 text-accent" />
|
|
Amenidades
|
|
</h3>
|
|
<ul className="mt-5 grid gap-3 sm:grid-cols-2">
|
|
{listaAmenidades.map((amenidad) => (
|
|
<li key={amenidad} className="flex items-start gap-2.5 text-sm text-[#374151] dark:text-[#CBD5E1]">
|
|
<span
|
|
aria-hidden="true"
|
|
className="mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-accent/10 text-accent dark:bg-accent/20"
|
|
>
|
|
<Check className="h-3.5 w-3.5" strokeWidth={3} />
|
|
</span>
|
|
<span>{amenidad}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|