import type { ReactNode } from 'react' import type { EstadoDesarrollo, TipoDesarrollo } from '../../services/desarrollos' // ─── Clases compartidas (mismo lenguaje visual que el resto de la landing) ── export const TARJETA = 'rounded-xl border border-[#E5E7EB] bg-white shadow-sm dark:border-[#1E293B] dark:bg-[#1E293B]' export const INPUT = 'mt-1.5 w-full rounded-lg border border-[#D1D5DB] bg-white px-3 py-2 text-sm text-[#111827] transition-colors focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20 dark:border-[#334155] dark:bg-[#0F172A] dark:text-[#E2E8F0]' export const INPUT_ERROR = 'border-red-500 dark:border-red-500' export const LABEL = 'block text-xs font-medium text-[#4B5563] dark:text-[#CBD5E1]' export const AYUDA = 'mt-1 text-[11px] text-[#64748B] dark:text-[#94A3B8]' export const TEXTO_ERROR = 'mt-1 text-xs text-red-600 dark:text-red-400' export const BOTON_PRIMARIO = 'rounded-lg bg-accent px-4 py-2 text-sm font-medium text-white shadow-sm transition-all duration-100 ease-out hover:bg-accent-hover disabled:opacity-50' export const BOTON_SECUNDARIO = 'rounded-lg border border-[#D1D5DB] bg-white px-4 py-2 text-sm font-medium text-[#374151] shadow-sm transition-all duration-100 ease-out hover:bg-[#F9FAFB] dark:border-[#334155] dark:bg-[#0F172A] dark:text-[#CBD5E1] dark:hover:bg-[#334155]' export const BOTON_PELIGRO = 'rounded-lg border border-red-300 bg-white px-4 py-2 text-sm font-medium text-red-600 shadow-sm transition-all duration-100 ease-out hover:bg-red-50 disabled:opacity-50 dark:border-red-500/40 dark:bg-[#0F172A] dark:text-red-400 dark:hover:bg-red-500/10' export const BOTON_MINIMO = 'rounded-lg border border-[#D1D5DB] bg-white px-3 py-1.5 text-xs font-medium text-[#374151] shadow-sm transition-colors hover:bg-[#F9FAFB] disabled:opacity-50 dark:border-[#334155] dark:bg-[#0F172A] dark:text-[#CBD5E1] dark:hover:bg-[#334155]' // ─── Etiquetas de dominio ─────────────────────────────────────────── export const ETIQUETA_ESTADO: Record = { ACTIVO: 'Activo', PREVENTA: 'Preventa', CERRADO: 'Cerrado', } const CLASE_ESTADO: Record = { ACTIVO: 'bg-green-50 text-green-700 dark:bg-green-500/10 dark:text-green-400', PREVENTA: 'bg-amber-50 text-amber-700 dark:bg-amber-500/10 dark:text-amber-400', CERRADO: 'bg-[#F1F5F9] text-[#475569] dark:bg-[#334155] dark:text-[#CBD5E1]', } export const ETIQUETA_TIPO: Record = { VERTICAL: 'Vertical', HORIZONTAL: 'Horizontal', } /** Base sobre la que se calcula un componente del precio ('lista' | 'final') */ export const ETIQUETA_BASE = { lista: 'precio de lista', final: 'precio final' } as const export const ETIQUETA_MODO_MENSUALIDADES = { remanente: 'sobre el remanente', remanenteLista: 'sobre el remanente del precio de lista', porcentaje: 'sobre el remanente en porcentaje', ninguna: 'sin mensualidades', } as const export const ETIQUETA_FUENTE_N = { fechaEntrega: 'Fecha de entrega de la unidad', fijo: 'N fijo (entrega inmediata / terminación de obra)', porUnidad: 'Capturado por unidad', } as const /** * Estatus posibles del inventario (los mismos que maneja el cotizador). * Un par canónico APARTADO/VENDIDO: las variantes femeninas APARTADA/VENDIDA * quedaron normalizadas en la base de datos; CLASE_ESTATUS las conserva para * pintar bien cualquier fila histórica que aún las traiga. */ export const ESTATUS_UNIDAD = [ 'DISPONIBLE', 'APARTADO', 'VENDIDO', 'ESCRITURADO', 'BLOQUEADO', ] as const const CLASE_ESTATUS: Record = { DISPONIBLE: 'bg-green-50 text-green-700 dark:bg-green-500/10 dark:text-green-400', APARTADO: 'bg-amber-50 text-amber-700 dark:bg-amber-500/10 dark:text-amber-400', APARTADA: 'bg-amber-50 text-amber-700 dark:bg-amber-500/10 dark:text-amber-400', VENDIDO: 'bg-blue-50 text-blue-700 dark:bg-blue-500/10 dark:text-blue-400', VENDIDA: 'bg-blue-50 text-blue-700 dark:bg-blue-500/10 dark:text-blue-400', ESCRITURADO: 'bg-indigo-50 text-indigo-700 dark:bg-indigo-500/10 dark:text-indigo-400', BLOQUEADO: 'bg-[#F1F5F9] text-[#475569] dark:bg-[#334155] dark:text-[#CBD5E1]', } export function BadgeEstado({ estado }: { estado: EstadoDesarrollo }) { return ( {ETIQUETA_ESTADO[estado] ?? estado} ) } export function BadgeEstatus({ estatus }: { estatus: string | null | undefined }) { if (!estatus) return — return ( {estatus} ) } export function Chip({ children }: { children: ReactNode }) { return ( {children} ) } // ─── Piezas de formulario ─────────────────────────────────────────── export function Campo({ id, etiqueta, requerido = false, ayuda, error, className = '', children, }: { id: string etiqueta: string requerido?: boolean ayuda?: string error?: string className?: string children: ReactNode }) { return (
{children} {ayuda &&

{ayuda}

} {error && (

{error}

)}
) } export function MensajeError({ children }: { children: ReactNode }) { return (

{children}

) } export function MensajeExito({ children }: { children: ReactNode }) { return (

{children}

) } /** Formato de moneda para precios de inventario */ export function moneda(valor: number | null | undefined): string { if (valor == null) return '—' return new Intl.NumberFormat('es-MX', { style: 'currency', currency: 'MXN', maximumFractionDigits: 0, }).format(valor) } /** Convierte '' | null | undefined a null y el resto a número (NaN → null) */ export function aNumero(valor: string): number | null { const limpio = valor.trim() if (limpio === '') return null const n = Number(limpio) return Number.isFinite(n) ? n : null } export function aTexto(valor: number | null | undefined): string { return valor == null ? '' : String(valor) }