feat: ecosistema FRASA — apps Cotizador, Alta Clientes, Cobranza y Landing

- Cotizador (/quote): 4 propuestas con formulas validadas del Excel, comparador,
  combobox de unidades, vigencia, PDF ANEXO A
- Alta Clientes (/client): wizard 8 pasos, validacion CURP RENAPO, OCR de INE,
  checklist de expediente, firma digital, 5 PDFs legales
- Cobranza (/pay): planes de enganche, mora, vista cobranza del dia, recibos PDF,
  bitacora, dashboard con filtros
- Landing (/): entrada al ecosistema
- Design system StudioBlank (monocromo, acento #1B4332) en las 4 apps
- 123 tests vitest
This commit is contained in:
2026-08-21 22:27:36 +00:00
commit ecd4edac02
147 changed files with 27345 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
import { useFormContext } from 'react-hook-form'
import { CampoNumero, CampoTexto } from '../components/Campos'
import SectionCard from '../components/SectionCard'
import { formatoMoneda } from '../lib/formatters'
interface Props {
prefix: string
}
/** Campos de datos económicos con totales calculados de solo lectura */
export default function EconomicosCampos({ prefix }: Props) {
const { watch } = useFormContext()
const p = (campo: string) => `${prefix}${campo}`
const ingresosMensuales = Number(watch(p('ingresosMensuales'))) || 0
const otrosIngresos = Number(watch(p('otrosIngresos'))) || 0
const gastosLuz = Number(watch(p('gastosLuz'))) || 0
const gastosOtros = Number(watch(p('gastosOtros'))) || 0
return (
<SectionCard titulo="Datos económicos">
<CampoNumero nombre={p('ingresosMensuales')} etiqueta="Ingresos mensuales" />
<CampoNumero nombre={p('otrosIngresos')} etiqueta="Otros ingresos" />
<label htmlFor={`${prefix}totalIngresos`} className="block">
<span className="mb-1 block text-xs font-bold uppercase tracking-widest text-neutral-500">Total ingresos</span>
<input
id={`${prefix}totalIngresos`}
type="text"
readOnly
value={formatoMoneda(ingresosMensuales + otrosIngresos)}
className="w-full border border-neutral-200 bg-neutral-50 px-3 py-2.5 text-sm font-bold text-neutral-900"
/>
</label>
<CampoNumero nombre={p('gastosLuz')} etiqueta="Gastos luz" />
<CampoNumero nombre={p('gastosOtros')} etiqueta="Otros gastos" />
<label htmlFor={`${prefix}gastosTotal`} className="block">
<span className="mb-1 block text-xs font-bold uppercase tracking-widest text-neutral-500">Gastos total</span>
<input
id={`${prefix}gastosTotal`}
type="text"
readOnly
value={formatoMoneda(gastosLuz + gastosOtros)}
className="w-full border border-neutral-200 bg-neutral-50 px-3 py-2.5 text-sm font-bold text-neutral-900"
/>
</label>
<CampoTexto nombre={p('dependientes.personas')} etiqueta="Dependientes económicos (personas)" />
<CampoTexto nombre={p('dependientes.parentesco')} etiqueta="Parentesco" />
<CampoTexto nombre={p('dependientes.edad')} etiqueta="Edad" />
</SectionCard>
)
}