thrive_utils
v0.0.1
Published
Componentes React 18 personalizados: formularios dinámicos, servicios API y autenticación JWT
Readme
thrive_utils
Librería React con componentes de formulario dinámico, servicios HTTP y autenticación JWT.
Instalación
npm install thrive_utilsPeer Dependencies
npm install react react-dom @mui/material @emotion/react @emotion/styled axios jwt-decode qsMódulos
📋 FieldDef — Definición de campos
Clase builder para definir campos de formulario de forma declarativa.
Tipos
type FieldType = 'input' | 'select' | 'checkbox' | 'textarea' | 'date';
interface FieldOption {
label: string;
value: string | number;
}Atributos
| Atributo | Tipo | Default | Descripción |
|---|---|---|---|
| type | FieldType | — | Tipo de campo |
| key | string | — | Identificador único del campo |
| label | string? | undefined | Etiqueta visible |
| placeholder | string? | undefined | Texto placeholder |
| typeFormat | string? | 'text' | Subtipo HTML (email, password, etc.) |
| required | boolean | false | Si es obligatorio |
| options | FieldOption[] | [] | Opciones para campos select |
| width | string \| number? | undefined | Ancho personalizado |
| rows | number | 4 | Filas para campos textarea |
| defaultChecked | boolean | false | Estado inicial para checkbox |
Métodos builder
Todos retornan this para encadenamiento.
| Método | Firma | Descripción |
|---|---|---|
| setRequired | (value?: boolean): this | Marca como obligatorio |
| setOptions | (items: Record[], labelKey?: string, valueKey?: string): this | Carga opciones de un array de objetos |
| setWidth | (value: string \| number): this | Establece ancho |
| setRows | (value: number): this | Establece filas del textarea |
| setDefaultChecked | (value?: boolean): this | Establece checked por defecto |
Ejemplo
import { FieldDef } from 'thrive_utils';
const campos = [
new FieldDef('input', 'nombre', 'Nombre completo', 'Ingresá tu nombre')
.setRequired(),
new FieldDef('input', 'email', 'Email', '', 'email')
.setRequired()
.setWidth('300px'),
new FieldDef('select', 'pais', 'País', 'Seleccioná...')
.setRequired()
.setOptions(paises, 'nombre', 'id'),
new FieldDef('date', 'fechaNac', 'Fecha de nacimiento'),
new FieldDef('textarea', 'observaciones', 'Observaciones', 'Notas...')
.setRows(6),
new FieldDef('checkbox', 'acepta', 'Acepto los términos')
.setDefaultChecked(false),
];🧩 Formulario — Componente de formulario dinámico
Renderiza una lista de FieldDef como campos MUI interactivos.
Props (FormularioProps)
interface FormularioProps {
fields: FieldDef[];
valores: Record<string, any>;
onInputChange: (campo: string, valor: any) => void;
tieneError: (campo: string) => boolean;
obtenerError: (campo: string) => string;
className?: string;
slots?: Record<string, React.ReactNode>;
}| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
| fields | FieldDef[] | ✅ | Definiciones de campos a renderizar |
| valores | Record<string, any> | ✅ | Valores actuales del formulario |
| onInputChange | (campo, valor) => void | ✅ | Callback cuando cambia un campo |
| tieneError | (campo) => boolean | ✅ | Retorna si un campo tiene error |
| obtenerError | (campo) => string | ✅ | Retorna el mensaje de error |
| className | string? | ❌ | Clase CSS adicional para el contenedor |
| slots | Record<string, ReactNode>? | ❌ | Contenido personalizado por campo |
Ejemplo
import { Formulario, FieldDef, useForm } from 'thrive_utils';
function MiFormulario() {
const campos = [
new FieldDef('input', 'nombre', 'Nombre').setRequired(),
new FieldDef('select', 'rol', 'Rol').setOptions([
{ id: 1, name: 'Admin' },
{ id: 2, name: 'Editor' },
]),
];
const { valores, handleChange, tieneError, obtenerError } = useForm({
nombre: '',
rol: '',
});
return (
<Formulario
fields={campos}
valores={valores}
onInputChange={handleChange}
tieneError={tieneError}
obtenerError={obtenerError}
className="grid grid-cols-2"
/>
);
}Slots personalizados
Reemplazá cualquier campo con contenido propio:
<Formulario
fields={campos}
valores={valores}
onInputChange={handleChange}
tieneError={tieneError}
obtenerError={obtenerError}
slots={{
avatar: <MiComponenteAvatar />,
}}
/>🪝 useForm<T> — Hook de gestión de formularios
Hook React para manejar estado, errores de backend y notificaciones de formularios.
Tipos
type TipoNotificacion = 'success' | 'error' | 'info' | 'warning';
interface NotificacionState {
abierta: boolean;
mensaje: string;
tipo: TipoNotificacion;
}
interface UseFormReturn<T> {
valores: T;
enviando: boolean;
setEnviando: (value: boolean) => void;
handleChange: (campo: string, valor: unknown) => void;
capturarErrores: (error: BackendError) => void;
tieneError: (campo: string) => boolean;
obtenerError: (campo: string) => string;
reset: () => void;
notificacion: NotificacionState;
abrirNotificacion: (mensaje: string, tipo?: TipoNotificacion) => void;
cerrarNotificacion: () => void;
}Retorno
| Propiedad | Tipo | Descripción |
|---|---|---|
| valores | T | Estado actual del formulario |
| enviando | boolean | Flag de envío en progreso |
| setEnviando | (boolean) => void | Controla el estado de envío |
| handleChange | (campo, valor) => void | Actualiza un campo (y limpia su error) |
| capturarErrores | (error) => void | Parsea errores de backend { response.data.errors } |
| tieneError | (campo) => boolean | Verifica si un campo tiene error |
| obtenerError | (campo) => string | Obtiene el mensaje de error |
| reset | () => void | Restaura valores y errores al estado inicial |
| notificacion | NotificacionState | Estado de la notificación |
| abrirNotificacion | (mensaje, tipo?) => void | Muestra notificación (default: 'success') |
| cerrarNotificacion | () => void | Cierra la notificación |
Ejemplo
import { useForm } from 'thrive_utils';
function CrearUsuario() {
const { valores, handleChange, capturarErrores, tieneError, obtenerError,
enviando, setEnviando, abrirNotificacion, reset } = useForm({
nombre: '',
email: '',
});
const guardar = async () => {
setEnviando(true);
try {
await api.post('/usuarios', valores);
abrirNotificacion('Usuario creado', 'success');
reset();
} catch (error) {
capturarErrores(error);
} finally {
setEnviando(false);
}
};
}🌐 ApiService — Cliente HTTP configurable
Wrapper sobre Axios con interceptores de autenticación, manejo de errores y soporte para uploads con FormData.
Configuración (ApiServiceConfig)
interface ApiServiceConfig {
baseURL: string;
getToken?: () => string | null;
onUnauthorized?: () => void;
onForbidden?: () => void;
}| Propiedad | Tipo | Requerido | Descripción |
|---|---|---|---|
| baseURL | string | ✅ | URL base de la API |
| getToken | () => string \| null | ❌ | Función que retorna el JWT actual |
| onUnauthorized | () => void | ❌ | Callback en respuesta HTTP 401 |
| onForbidden | () => void | ❌ | Callback en respuesta HTTP 403 |
Atributos
| Atributo | Tipo | Default | Descripción |
|---|---|---|---|
| mostrarMensajes | boolean | true | Habilita logs de errores 404/422 en consola |
Métodos
| Método | Firma | Retorna | Descripción |
|---|---|---|---|
| get | <T>(uri, params?) | Promise<AxiosResponse<T>> | GET con respuesta Axios completa |
| getData | <T>(uri, params?) | Promise<T> | GET → response.data.data |
| getRaw | <T>(uri, params?) | Promise<T> | GET → response.data |
| getRawData | (uri, responseType, params?) | Promise<AxiosResponse> | GET con responseType (blob, arraybuffer) |
| getAll | <T>(uri, params?) | Promise<AxiosResponse<T>> | GET con limit=0 |
| getAllData | <T>(uri, params?) | Promise<T> | GET con limit=0 → response.data.data |
| post | <T>(uri, body, options?) | Promise<T> | POST (auto-detecta File → FormData) |
| put | <T>(uri, body, options?) | Promise<T> | PUT (auto-detecta File → FormData) |
| patch | <T>(uri, body, options?) | Promise<AxiosResponse<T>> | PATCH |
| delete | <T>(uri) | Promise<T> | DELETE |
Ejemplo
import { createApiService } from 'thrive_utils';
const api = createApiService({
baseURL: 'https://mi-api.com/api',
getToken: () => sessionStorage.getItem('accessToken'),
onUnauthorized: () => window.location.href = '/login',
onForbidden: () => alert('Sin permisos'),
});
// GET simple
const usuarios = await api.getData<Usuario[]>('/usuarios');
// POST con archivo
const file = new File([blob], 'foto.jpg');
await api.post('/documentos', { titulo: 'Mi doc', archivo: file });
// GET con parámetros
const resultado = await api.getData('/buscar', { q: 'juan', page: 1 });🔐 AuthService — Servicio de autenticación JWT
Gestiona sesión de usuario, tokens JWT y verificación de roles.
Tipos
interface RolUsuario {
id: number;
nombre: string;
}
interface User {
id: number;
nombreCompleto: string;
email?: string;
roles: RolUsuario[];
}Métodos
| Método | Firma | Retorna | Descripción |
|---|---|---|---|
| setAccessToken | (token: string) | JWTPayload | Decodifica JWT, guarda token y usuario |
| setRefreshToken | (token: string) | void | Guarda refresh token |
| getAccessToken | () | string \| null | Retorna access token |
| getRefreshToken | () | string \| null | Retorna refresh token |
| getToken | () | string \| null | Alias de getAccessToken |
| getUser | () | User \| null | Retorna usuario actual |
| isAuthenticated | () | boolean | Verifica si hay sesión activa |
| estaLogueado | () | boolean | Alias de isAuthenticated |
| removeSessionData | () | void | Limpia todos los datos de sesión |
| setState | (response) | void | Establece sesión desde respuesta del backend |
| getRolUsuario | () | RolUsuario \| null | Retorna el primer rol del usuario |
| getRolUsuarios | () | RolUsuario[] | Retorna todos los roles |
| hasRole | (nombre: string) | boolean | Verifica si el usuario tiene un rol |
| setSessionData | (authData) | void | Establece sesión desde objeto { user, token } |
Ejemplo
import { AuthService } from 'thrive_utils';
const auth = new AuthService();
// Después del login
auth.setState({
access_token: tokenDelBackend,
refresh_token: refreshTokenDelBackend,
});
// Verificar sesión
if (auth.isAuthenticated()) {
const user = auth.getUser();
console.log(`Hola, ${user.nombreCompleto}`);
}
// Verificar roles
if (auth.hasRole('admin')) {
// mostrar panel de administración
}
// Logout
auth.removeSessionData();También se exporta una instancia singleton:
import { authService } from 'thrive_utils';
// Uso directo sin instanciar
authService.isAuthenticated();Scripts
npm test # Ejecutar tests
npm run test:coverage # Tests + reporte de cobertura
npm run build:lib # Build del paquete (ESM + CJS + tipos)Exports
// Componentes
export { Formulario, FieldDef, useForm }
// Servicios
export { ApiService, createApiService, AuthService, authService }
// Tipos
export type {
FormularioProps, // Props del componente Formulario
FieldType, // 'input' | 'select' | 'checkbox' | 'textarea' | 'date'
FieldOption, // { label, value }
TipoNotificacion, // 'success' | 'error' | 'info' | 'warning'
NotificacionState, // Estado de notificación
UseFormReturn, // Retorno del hook useForm
ApiServiceConfig, // Configuración de ApiService
RolUsuario, // { id, nombre }
User, // Usuario autenticado
}Requisitos
- Node.js ≥ 20.x
- npm ≥ 10.x
- React ≥ 18
- MUI ≥ 7
Cobertura de Tests
| Módulo | Stmts | Branch | Funcs | Lines |
|-----------------|-------|--------|-------|-------|
| FieldDef.ts | 100% | 100% | 100% | 100% |
| Formulario.tsx | 100% | 100% | 100% | 100% |
| useForm.ts | 100% | 100% | 100% | 100% |
| api-service.ts | 100% | 100% | 100% | 100% |
| auth-service.ts | 100% | 100% | 100% | 100% |