12 KiB
Mi presentación — Frontend + UX
Esto es lo que voy a decir en la exposición.
1. ¿Qué es el proyecto y qué aporta mi parte?
El proyecto es un dashboard de gestión de proyectos que se conecta a Dolibarr, un ERP de código abierto. Dolibarr tiene los datos, pero su interfaz no está pensada para el uso diario de alguien que necesita tomar decisiones rápidas.
Mi parte es la interfaz web. Una interfaz moderna que muestra los proyectos de forma clara y visual, permite buscar, filtrar y exportar, da el detalle completo de cada proyecto, y además es responsive, accesible y rápida.
El valor que aporto es transformar datos crudos de un ERP en una experiencia de usuario limpia y eficiente.
2. Stack tecnológico
Las tecnologías que uso son:
- Next.js 16 — el framework principal. Tiene server components para carga rápida y App Router para rutas limpias.
- TypeScript — tipado estricto. Evita errores y hace el código más mantenible.
- Tailwind CSS — diseño utility-first. No escribo CSS suelto, todo son clases en el propio componente.
- shadcn/ui — componentes accesibles y personalizables: botones, cards, badges, tabs, inputs.
- Lucide React — iconos.
¿Por qué Next.js y no React plano? Porque Next.js te da server components para que la carga inicial sea más rápida, App Router para organizar las rutas de forma limpia, y se integra muy bien con TypeScript y Tailwind.
3. Arquitectura de la UI — flujo de datos
El flujo de datos es siempre el mismo. Lo explico con este esquema:
Dolibarr envía datos crudos a través de su API. Esos datos pasan por una capa de transformación, que es el mapper. El mapper los normaliza y se los pasa a los componentes de UI. Y los componentes construyen las vistas que ve el usuario.
Cada capa tiene una responsabilidad única. Si cambia la API de Dolibarr, solo toco el mapper. Si cambia el diseño, solo toco los componentes. Si cambia la página, solo toco la vista.
Esto es escalabilidad: puedo añadir nuevos módulos sin romper lo que ya funciona.
4. Vista de mando — el dashboard
La pantalla principal es el dashboard. Tiene tres posibles estados.
Mientras los datos están cargando, se muestran skeletons. Si hay un error, aparece un banner con un botón de reintentar. Y cuando los datos llegan correctamente, se renderizan los KPIs arriba y el grid de tarjetas abajo.
El usuario lee los indicadores clave y, si quiere entrar en detalle, hace clic en cualquier tarjeta y navega al proyecto.
if (isLoading) return <ProjectGridSkeleton />;
if (error) return <ErrorBanner onRetry={refetch} />;
return <StatsOverview /> + <ProjectGrid />;
5. KPIs — los indicadores clave
Los KPIs son los números grandes que resumen el estado global del portfolio.
En mi caso son tres: proyectos activos, presupuesto total y progreso medio.
La clave es la jerarquía visual. El número va en grande, con tipografía destacada y color. Debajo, una etiqueta corta y un texto de comparación con el periodo anterior.
<div className="text-4xl font-bold text-blue-600">94,2%</div>
<p className="text-xs text-muted-foreground">+2.1% respecto al mes anterior</p>
El número es lo primero que se lee. Luego el contexto. El usuario detecta tendencias en segundos, sin tener que leer párrafos.
6. Grid y List — dos formas de ver los proyectos
El dashboard permite cambiar entre dos vistas con un toggle. Grid, que son tarjetas visuales, o lista, que es una tabla con más densidad de información.
const [view, setView] = useState<"grid" | "list">("grid");
{view === "grid" ? <ProjectGrid /> : <ProjectList />}
El grid es mejor para exploración visual, porque las tarjetas tienen colores y badges que se identifican rápido. La lista es mejor para revisar muchos proyectos seguidos, porque muestra más campos en una misma línea.
El cambio es instantáneo y no se pierde el contexto: los filtros aplicados se mantienen al cambiar de vista.
7. Project Card — la unidad visual básica
La tarjeta de proyecto es el componente que más veces se renderiza en toda la aplicación.
Cada tarjeta tiene el estado con un badge de color, la barra de progreso, el presupuesto y el responsable.
<Badge className={getStatusColorClasses(project.status)}>
{STATUS_CONFIG[project.status].label}
</Badge>
<Progress value={project.progress} className="h-2" />
<span>{project.budget.toLocaleString('es-ES')} €</span>
<span>{project.responsible}</span>
Los colores de los badges siguen un código semafórico: gris para borrador, azul para abierto, verde para cerrado. Esto permite identificar el estado sin leer la etiqueta.
8. Toolbar — buscar, filtrar, exportar
En la vista operativa, la barra de herramientas concentra todo lo que el usuario necesita para trabajar con el listado de proyectos.
Un input para buscar por texto, un selector para filtrar por estado, y un botón para exportar a PDF.
<Input placeholder="Buscar proyectos..." />
<Select>
<SelectItem value="0">Borrador</SelectItem>
<SelectItem value="1">Abierto</SelectItem>
<SelectItem value="2">Cerrado</SelectItem>
</Select>
<Button onClick={exportToPdf}>Exportar</Button>
Todo en una sola barra. El usuario no tiene que ir a menús laterales ni a otras pantallas para hacer estas operaciones.
9. Filtrado ultrarrápido con useMemo
Cuando el usuario escribe en el buscador, el filtrado es instantáneo. No hay llamadas al servidor, no hay recarga de página.
Esto se consigue con useMemo. El hook recuerda el resultado del filtrado y solo lo recalcula cuando cambian los proyectos, el texto de búsqueda o el filtro de estado.
const filteredProjects = useMemo(() => {
return projects.filter(project => {
const matchesSearch = project.title
.toLowerCase()
.includes(searchQuery.toLowerCase());
const matchesStatus = !statusFilter || project.status === statusFilter;
return matchesSearch && matchesStatus;
});
}, [projects, searchQuery, statusFilter]);
La respuesta es inmediata incluso con listas grandes. Esto es clave para la percepción de rendimiento: la aplicación se siente rápida, y el usuario no pierde el flujo de trabajo.
10. Vista de detalle 360 grados
Cuando el usuario hace clic en un proyecto, no navega a una página diferente. Todo está organizado en una sola pantalla.
La cabecera muestra el nombre, el estado y las acciones principales. Después viene un bloque de estadísticas y progreso. Y luego los tabs para organizar el resto del contenido.
<div>
<ProjectHeader />
<ProjectStats />
<Tabs>
<TasksSection />
<ProjectTaskGantt />
<ProjectInfo />
</Tabs>
</div>
El usuario puede ver las tareas, la planificación temporal y la información del proyecto sin cambiar de página. Todo está a un clic de distancia.
11. Tabs y segmentación de la información
Los tabs no solo organizan el contenido: lo segmentan por contexto.
Cada pestaña tiene un propósito claro. Tareas para la parte operativa. Gantt para la planificación temporal. Información para los datos generales del proyecto.
El usuario solo procesa lo que necesita en cada momento. Esto reduce la carga cognitiva y hace que la interfaz sea más fácil de usar.
12. Data mapping — de Dolibarr a la UI
Esta es una de las partes más importantes a nivel técnico.
Dolibarr no está pensado para ser consumido por una interfaz moderna. Devuelve los datos en un formato que no es directamente usable. Los estados son números: 0, 1, 2. Las fechas vienen como timestamp en segundos. El presupuesto es un número sin formato.
Mi trabajo es transformar esos datos en algo que la interfaz pueda mostrar.
export const STATUS_CONFIG = {
"0": { label: "Borrador", color: "bg-gray-100 text-gray-700" },
"1": { label: "Abierto", color: "bg-blue-100 text-blue-700" },
"2": { label: "Cerrado", color: "bg-green-100 text-green-700" },
};
export function mapDolibarrProject(dolibarr) {
return {
id: dolibarr.id,
title: dolibarr.title,
status: String(dolibarr.status) as ProjectStatus,
progress: Number(dolibarr.progress) || 0,
budget: Number(dolibarr.budget) || 0,
dateStart: new Date(dolibarr.date_start * 1000).toLocaleDateString('es-ES'),
};
}
Hay un detalle importante aquí. Los timestamps de Dolibarr vienen en segundos, pero JavaScript trabaja con milisegundos. Por eso multiplico por mil. Si no hiciera esto, las fechas aparecerían como el 1 de enero de 1970.
Esto es un ejemplo de algo que te encuentras cuando trabajas con APIs que no siguen el estándar de JavaScript. Pequeños detalles que hay que tener en cuenta para que la interfaz funcione correctamente.
13. Skeletons — estados de carga
Mientras los datos se están cargando, no muestro una pantalla vacía ni un spinner genérico. Muestro placeholders con la misma estructura que el contenido real.
{Array.from({ length: 6 }).map((_, i) => (
<Card key={i}>
<Skeleton className="h-4 w-20 rounded-full" />
<Skeleton className="h-6 w-3/4" />
<Skeleton className="h-2 w-full" />
</Card>
))}
El usuario sabe qué tipo de contenido va a aparecer y en qué posición. La aplicación nunca se ve vacía. Esto mejora la percepción de rendimiento: aunque la red sea lenta, la experiencia se siente fluida.
14. Tolerancia a fallos
¿Qué pasa si Dolibarr se cae o hay un error de red?
No dejo al usuario con una pantalla en blanco ni con un error críptico. Muestro un banner con un mensaje claro y un botón de reintentar.
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error de conexión</AlertTitle>
<AlertDescription>
No se pudieron cargar los proyectos.
<Button variant="outline" size="sm" onClick={onRetry}>
Reintentar
</Button>
</AlertDescription>
</Alert>
El error se comunica de forma clara, la interfaz no se rompe y el usuario puede recuperarse con un solo clic. Esto mantiene la confianza en la aplicación incluso cuando algo falla.
15. Sistema de diseño
Para mantener la coherencia visual en toda la aplicación uso un sistema de diseño basado en tokens CSS. Los colores, las fuentes y los espaciados están definidos en un solo archivo.
:root {
--primary: 221.2 83.2% 53.3%;
--radius: 0.75rem;
}
Todos los componentes vienen de shadcn/ui y se personalizan con Tailwind. No hay CSS suelto en ningún sitio. Esto hace que cualquier cambio global se pueda aplicar desde las variables, sin tener que tocar componente por componente.
16. Conclusiones — las tres ideas clave
Para cerrar, resumo mi parte en tres ideas.
Primera, escalabilidad. La arquitectura está dividida en capas con responsabilidades claras. Si cambia la API, solo toco el mapper. Si cambia el diseño, solo toco los componentes. Se pueden añadir nuevos módulos sin reestructurar lo existente.
Segunda, experiencia de usuario. El diseño prioriza la lectura rápida con KPIs, el filtrado instantáneo con useMemo, y la navegación sin fricción. El usuario encuentra la información en menos pasos y con menos esfuerzo.
Y tercera, el frontend está construido con un stack moderno: Next.js, TypeScript, Tailwind y un sistema de diseño que garantiza consistencia visual en toda la aplicación.
Muchas gracias.
Apéndice: preguntas que me pueden hacer
¿Cuál fue el mayor reto? El data mapping. Dolibarr no está pensado para una UI moderna. Los timestamps en segundos, los estados como números, campos que pueden venir nulos... todo eso hay que normalizarlo para que la interfaz sea fiable.
¿Qué mejorarías? Tests automatizados. Ahora mismo no hay tests configurados. Añadiría tests unitarios con Vitest y de componentes con React Testing Library.
¿Por qué Next.js y no React plano? Por el App Router para rutas limpias, los server components para carga inicial rápida, y la buena integración con TypeScript y Tailwind.
¿Cómo aseguras consistencia visual? Con tokens CSS, componentes de shadcn/ui reutilizables y Tailwind sin CSS ad-hoc. Todo viene de un mismo sistema de diseño.