style: rediseno de layout profesional en las 7 apps
Headers sticky con marca y acciones, contenedores max-w-[1280px], grids de contenido con rails laterales sticky (cotizador, mi-dia, detalle de cobranza), stepper vertical en el wizard de alta, toolbar+ tabla unificadas en cobranza, tabs como segmented control en dashboards, chat con barra de entrada fija y preguntas sugeridas, landing con navbar + hero + grid de modulos + footer.
This commit is contained in:
@@ -7,6 +7,13 @@ import type { Mensaje } from '../types'
|
||||
let contadorLocal = 0
|
||||
const idLocal = () => `local-${++contadorLocal}`
|
||||
|
||||
const PREGUNTAS_SUGERIDAS = [
|
||||
'¿Qué incluye la propuesta del cotizador DIAGONAL?',
|
||||
'¿Cuáles son los pasos del proceso de venta?',
|
||||
'¿Qué políticas de Frasa debo conocer?',
|
||||
'¿Qué modelos hay en el catálogo DIAGONAL?',
|
||||
]
|
||||
|
||||
export default function ChatPage() {
|
||||
const [sessionId, setSessionId] = useState<string | null>(null)
|
||||
const [mensajes, setMensajes] = useState<Mensaje[]>([])
|
||||
@@ -70,8 +77,8 @@ export default function ChatPage() {
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-[#F8FAFC] text-[#111827]">
|
||||
<header className="border-b border-[#E5E7EB] bg-white">
|
||||
<div className="mx-auto flex w-full max-w-3xl items-center justify-between px-4 py-4">
|
||||
<header className="sticky top-0 z-10 border-b border-[#E5E7EB] bg-white/95 backdrop-blur">
|
||||
<div className="mx-auto flex h-14 w-full max-w-3xl items-center justify-between px-4 sm:px-6">
|
||||
<span className="text-sm tracking-wide">
|
||||
<span className="font-bold">FRASA</span>
|
||||
<span className="text-[#F97316]"> · </span>
|
||||
@@ -80,14 +87,14 @@ export default function ChatPage() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={nuevaConversacion}
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-5 py-2.5 text-xs font-medium text-[#374151] shadow-sm transition-all duration-100 ease-out hover:bg-[#F9FAFB]"
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-4 py-2 text-xs font-medium text-[#374151] shadow-sm transition-all duration-100 ease-out hover:bg-[#F9FAFB]"
|
||||
>
|
||||
Nueva conversación
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="mx-auto flex w-full max-w-3xl flex-1 flex-col px-4 py-8">
|
||||
<main className="mx-auto flex w-full max-w-3xl flex-1 flex-col px-4 py-8 sm:px-6">
|
||||
{sinConexion && (
|
||||
<p className="mb-6 rounded-lg bg-amber-50 px-4 py-3 text-xs font-medium text-amber-700">
|
||||
Sin conexión al API del asistente. Revisa que el servicio esté disponible e inténtalo de
|
||||
@@ -101,60 +108,76 @@ export default function ChatPage() {
|
||||
)}
|
||||
|
||||
{mensajes.length === 0 && !escribiendo ? (
|
||||
<div className="flex flex-1 flex-col items-center justify-center gap-4 text-center">
|
||||
<p className="text-xs font-semibold uppercase tracking-wider text-[#64748B]">
|
||||
Asistente interno
|
||||
</p>
|
||||
<p className="max-w-md text-2xl font-light text-[#111827]">
|
||||
Pregunta sobre el catálogo DIAGONAL, las propuestas del cotizador, el proceso de venta
|
||||
o las políticas de Frasa.
|
||||
</p>
|
||||
<div className="flex flex-1 flex-col items-center justify-center">
|
||||
<div className="w-full max-w-lg rounded-xl border border-[#E5E7EB] bg-white p-6 text-center shadow-sm">
|
||||
<p className="text-xs font-semibold uppercase tracking-wider text-[#64748B]">
|
||||
Asistente interno
|
||||
</p>
|
||||
<p className="mt-3 text-xl font-light leading-relaxed text-[#111827]">
|
||||
Pregunta sobre el catálogo DIAGONAL, las propuestas del cotizador, el proceso de
|
||||
venta o las políticas de Frasa.
|
||||
</p>
|
||||
<div className="mt-6 grid gap-2 sm:grid-cols-2">
|
||||
{PREGUNTAS_SUGERIDAS.map((pregunta) => (
|
||||
<button
|
||||
key={pregunta}
|
||||
type="button"
|
||||
onClick={() => setEntrada(pregunta)}
|
||||
className="rounded-lg border border-[#D1D5DB] bg-white px-3 py-2.5 text-left text-xs font-medium text-[#374151] transition-colors hover:border-accent hover:text-accent"
|
||||
>
|
||||
{pregunta}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-1 flex-col gap-6">
|
||||
{mensajes.map((m) => (
|
||||
<Burbuja key={m.id} mensaje={m} />
|
||||
))}
|
||||
{escribiendo && (
|
||||
<div className="flex justify-start">
|
||||
<p className="rounded-full bg-white px-4 py-2 text-xs font-medium text-[#64748B] shadow-sm">
|
||||
Escribiendo…
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div ref={finalRef} />
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
|
||||
<footer className="border-t border-[#E5E7EB] bg-white">
|
||||
<form
|
||||
className="mx-auto flex w-full max-w-3xl items-stretch gap-4 px-4 py-4"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
void enviar()
|
||||
}}
|
||||
>
|
||||
<label htmlFor="entrada-chat" className="sr-only">
|
||||
Escribe tu mensaje
|
||||
</label>
|
||||
<input
|
||||
id="entrada-chat"
|
||||
type="text"
|
||||
value={entrada}
|
||||
onChange={(e) => setEntrada(e.target.value)}
|
||||
placeholder="Escribe tu pregunta…"
|
||||
autoComplete="off"
|
||||
className="flex-1 rounded-lg border border-[#D1D5DB] bg-white px-4 py-3 text-sm placeholder:text-[#94A3B8] transition-colors focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={escribiendo || entrada.trim().length === 0}
|
||||
className="rounded-lg bg-accent px-6 py-3 text-sm font-medium text-white shadow-sm transition-all duration-100 ease-out hover:bg-accent-hover disabled:opacity-40"
|
||||
<footer className="sticky bottom-0 border-t border-[#E5E7EB] bg-white">
|
||||
<div className="relative mx-auto w-full max-w-3xl px-4 sm:px-6">
|
||||
{escribiendo && (
|
||||
<div className="pointer-events-none absolute -top-11 left-4 sm:left-6">
|
||||
<p className="rounded-full border border-[#E5E7EB] bg-white px-4 py-2 text-xs font-medium text-[#64748B] shadow-md">
|
||||
Escribiendo…
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<form
|
||||
className="flex items-stretch gap-3 py-4"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
void enviar()
|
||||
}}
|
||||
>
|
||||
Enviar
|
||||
</button>
|
||||
</form>
|
||||
<label htmlFor="entrada-chat" className="sr-only">
|
||||
Escribe tu mensaje
|
||||
</label>
|
||||
<input
|
||||
id="entrada-chat"
|
||||
type="text"
|
||||
value={entrada}
|
||||
onChange={(e) => setEntrada(e.target.value)}
|
||||
placeholder="Escribe tu pregunta…"
|
||||
autoComplete="off"
|
||||
className="flex-1 rounded-lg border border-[#D1D5DB] bg-white px-4 py-3 text-sm placeholder:text-[#94A3B8] transition-colors focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={escribiendo || entrada.trim().length === 0}
|
||||
className="rounded-lg bg-accent px-6 py-3 text-sm font-medium text-white shadow-sm transition-all duration-100 ease-out hover:bg-accent-hover disabled:opacity-40"
|
||||
>
|
||||
Enviar
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user