Componentes Web
Catálogo de componentes reutilizables del dashboard Vibra Web.
ListPageLayout
components/ui/list-page-layout.tsx
Layout genérico para páginas de listado con tabla, paginación y búsqueda. Elimina la duplicación de código entre módulos.
Props
interface ListPageLayoutProps<T> {
title: string;
subtitle: string;
data: T[];
total: number;
currentPage: number;
pageSize: number;
isLoading: boolean;
onPageChange: (page: number) => void;
onPageSizeChange: (size: number) => void;
onRefresh: () => void;
onAdd?: () => void;
addLabel?: string;
columns: Column<T>[];
rowKey: (item: T) => string | number;
actions?: ListAction<T>[];
searchEntity?: string;
onSearchData?: (data: any[]) => void;
onSearchLoading?: (loading: boolean) => void;
emptyMessage?: string;
deleteConfirm?: DeleteConfirm | null;
}
Columnas
interface Column<T> {
key: string;
label: string;
render: (item: T) => React.ReactNode;
className?: string;
}
Acciones
interface ListAction<T> {
icon: React.ReactNode;
tooltip: string;
onClick: (item: T) => void;
color?: string;
show?: (item: T) => boolean; // condicional
}
Características
<CurrentDateTime />en header- Búsqueda integrada vía componente
Search(si se proveensearchEntity,onSearchData,onSearchLoading) - Botón "Agregar" azul con
PlusCircleIcon - Tabla con hover
bg-blue-50 - Spinner animado durante carga
<Pagination>integrada<ModalConfirm>para confirmación de delete/toggle- Redirección a
/layoutsi no hay token
Módulos que lo usan
ActivityDataPage, ParticipantDataPage (y otros como user, company, role, permission, contact, config, preTest, permissionTemplate).
FormPageLayout
components/ui/form-page-layout.tsx
Layout genérico para páginas de formulario (crear/editar). Wrapper con header, auth check y botones Cancelar/Guardar.
Props
interface FormPageLayoutProps {
title: string;
isEditing: boolean;
isSubmitting: boolean;
onSubmit: (e: React.FormEvent) => void;
onCancel: () => void;
children: React.ReactNode;
submitLabel?: string;
cancelLabel?: string;
}
Características
- Header con título
- Auth guard: redirige a
/layoutsi no hay token - Botón Cancelar (gris con borde, ícono
XCircleIcon) - Botón Guardar (verde, ícono
SaveIcon, disabled durante envío) - Estado loading con texto "Guardando..."
Módulos que lo usan
ParticipantComponent, y puede usarse en cualquier formulario que necesite consistencia visual.
SearchableSelect
components/forms/searchable-select.tsx
Componente de selección con búsqueda asíncrona y debounce. Similar a un <select> pero con capacidad de buscar en el servidor.
Props
interface SearchableSelectProps<T extends { _id: string }> {
label: string;
placeholder?: string;
searchFn: (term: string) => Promise<T[]>;
renderOption: (item: T) => React.ReactNode;
getOptionValue?: (item: T) => string;
value: string;
onChange: (value: string, item?: T) => void;
required?: boolean;
disabled?: boolean;
minSearchLength?: number;
debounceMs?: number;
initialSelectedItem?: T | null;
}
Características
- Dos modos: "búsqueda" (input con icono search) y "seleccionado" (item con botón limpiar)
- Debounce: 300ms por defecto, configurable
- Navegación por teclado: ArrowUp/ArrowDown para moverse, Enter para seleccionar, Escape para cerrar
- Highlight: item destacado con fondo azul y texto blanco
- Loading: spinner animado durante la búsqueda
- Click outside: cierra el dropdown automáticamente
- Preselección: acepta
initialSelectedItempara evitar llamada API extra en edición
Módulos que lo usan
CourseFormPage para seleccionar institución (searchCompanies) e instructor (searchDocentes).
PermissionPicker
components/permission/permission-picker.tsx
Selector de permisos con búsqueda y agrupación por categoría. Usado en formularios de roles y templates de permisos.
Props
interface PermissionPickerProps {
selectedIds: string[];
onChange: (ids: string[]) => void;
label?: string;
}
Características
- Carga permisos vía
getAllPermissions(1, 200)al montar - Búsqueda local por nombre, serial o descripción (filtra en cliente)
- Agrupación por
permissionCategory.namecon headers enbg-gray-50y texto uppercase - Checkboxes con contador de seleccionados
- Scroll infinito visual (max-h-80 con overflow)
- Estados: loading ("Cargando permisos..."), empty ("No hay permisos disponibles"), sin resultados ("Sin resultados")
Módulos que lo usan
PermissionTemplate para asignar permisos a templates.
CardSection
components/ui/card-section.tsx
Sección contenedora con header degradado y cuerpo. Usada para agrupar campos de formulario en secciones visualmente distintas.
Props
interface CardSectionProps {
title: string;
subtitle?: string;
children: React.ReactNode;
}
Características
- Header con
bg-gradient-to-r from-blue-50 to-white - Borde inferior separador
- Padding interior de 6 (1.5rem) en el cuerpo
- Sombra suave (
shadow-sm) y bordeborder-gray-200 - Subtítulo opcional en gris
Módulos que lo usan
ActivityComponent para las secciones: Información Principal, Programación, Recursos Multimedia, Preguntas, Tips de Apoyo Emocional, Juegos.
FloatingFeedbackBtn
components/ui/floating-feedback-btn.tsx
Botón de acción flotante (FAB) para enviar feedback desde cualquier página del dashboard. Implementa [vibra-073].
Características
- Posición:
fixed bottom-24 right-6 z-50 - Botón principal: círculo índigo
w-14 h-14con ícono 📝 - Al abrir, muestra dos opciones expandidas:
- 💬 Enviar apoyo — hover verde
- ✨ Sugerir mejora — hover azul
- Al seleccionar, abre
FeedbackModalcon el tipo correspondiente - Envía
POST /api/feedbackcon{ title, description, isFeature, isSupport, createdBy } - Toast de éxito/error con
sonner - Transiciones
transition-all duration-200yactive:scale-95