Files
FRASA-CAS/apps/landing/src/App.tsx
Consult AS Technology dc6ec786d0 fix(responsive): layout 100% width en todas las resoluciones
- Eliminado max-w-[1280px] de todas las apps (mi-dia, landing, cotizador, cobranza, dashboards, alta-clientes)
- Kanban: sin restricción de ancho, columnas min-w-[220px]
- Grids: xl:grid-cols-4/5 para pantallas grandes
- CSS base: -webkit-text-size-adjust + min-h-screen
- 6 apps construidas
2026-09-08 08:36:53 +00:00

140 lines
4.8 KiB
TypeScript

import { useEffect, useState } from 'react'
import { MODULOS, esModuloVisible } from './data/modulos'
import { Navbar } from './components/Navbar'
import { TarjetaModulo } from './components/TarjetaModulo'
import { ConfiguracionCuenta } from './components/ConfiguracionCuenta'
import BannerModoLocal from './components/BannerModoLocal'
import { VistaContactos } from './components/VistaContactos'
import { ConfigPage } from './components/config/ConfigPage'
import { CapturaPage } from './pages/CapturaPage'
import { ClientesMaestroPage } from './pages/ClientesMaestroPage'
import { getToken, type UsuarioSesion } from './services/auth'
import { getMe } from './services/api'
type Vista = 'home' | 'contactos' | 'captura' | 'clientes-maestro' | 'configuraciones'
export default function App() {
const [vista, setVista] = useState<Vista>('home')
// Módulos permitidos por el rol (GET /auth/me); null = sin sesión → se muestran todos
const [permitidos, setPermitidos] = useState<string[] | null>(null)
const [usuario, setUsuario] = useState<UsuarioSesion | null>(null)
useEffect(() => {
let cancelado = false
const cargar = () => {
if (!getToken()) {
setPermitidos(null)
setUsuario(null)
return
}
// Si /me falla se conserva lo anterior (o todos): el login wall de cada app sigue
getMe()
.then((u) => {
if (!cancelado) {
setPermitidos(u.modulos ?? [])
setUsuario(u)
}
})
.catch(() => {})
}
cargar()
window.addEventListener('frasa:sesion', cargar)
return () => {
cancelado = true
window.removeEventListener('frasa:sesion', cargar)
}
}, [])
if (vista === 'contactos') {
return (
<div className="flex min-h-screen flex-col">
<Navbar usuario={usuario} onConfiguraciones={() => setVista('configuraciones')} />
<BannerModoLocal />
<VistaContactos onVolver={() => setVista('home')} />
</div>
)
}
if (vista === 'captura') {
return (
<div className="flex min-h-screen flex-col">
<Navbar usuario={usuario} onConfiguraciones={() => setVista('configuraciones')} />
<BannerModoLocal />
<CapturaPage onVolver={() => setVista('home')} />
</div>
)
}
if (vista === 'clientes-maestro') {
return (
<div className="flex min-h-screen flex-col">
<Navbar usuario={usuario} onConfiguraciones={() => setVista('configuraciones')} />
<BannerModoLocal />
<ClientesMaestroPage onVolver={() => setVista('home')} />
</div>
)
}
if (vista === 'configuraciones' && usuario?.rol === 'admin') {
return (
<div className="flex min-h-screen flex-col">
<Navbar usuario={usuario} onConfiguraciones={() => setVista('configuraciones')} />
<BannerModoLocal />
<ConfigPage onVolver={() => setVista('home')} />
</div>
)
}
return (
<div className="flex min-h-screen flex-col">
<Navbar usuario={usuario} onConfiguraciones={() => setVista('configuraciones')} />
<BannerModoLocal />
<main className="w-full flex-1 px-4 sm:px-6 lg:px-8">
<section aria-labelledby="titulo-hero" className="py-12 sm:py-16">
<h1
id="titulo-hero"
className="text-3xl font-semibold tracking-tight text-[#111827] dark:text-[#E2E8F0] sm:text-4xl"
>
Ecosistema comercial
</h1>
<p className="mt-3 max-w-2xl text-sm leading-relaxed text-[#64748B] dark:text-[#94A3B8] sm:text-base">
Las herramientas del equipo comercial de Frasa Desarrollos en un solo lugar:
cotizaciones, alta de clientes, cobranza, tableros y asistente IA.
</p>
</section>
<nav aria-label="Módulos del ecosistema">
<ul className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{MODULOS.filter((modulo) => esModuloVisible(modulo, permitidos)).map((modulo) => (
<TarjetaModulo
key={modulo.id}
modulo={modulo}
onAbrir={
modulo.id === 'contactos'
? () => setVista('contactos')
: modulo.id === 'captura'
? () => setVista('captura')
: modulo.id === 'clientes-maestro'
? () => setVista('clientes-maestro')
: undefined
}
/>
))}
</ul>
</nav>
<div className="mt-6">
<ConfiguracionCuenta />
</div>
</main>
<footer className="mt-16 border-t border-[#E5E7EB] dark:border-[#1E293B]">
<div className="px-6 py-8 sm:px-6 lg:px-8">
<p className="text-xs text-[#64748B] dark:text-[#94A3B8]">Consult (AS) Technology</p>
</div>
</footer>
</div>
)
}