UNPKG

push-orbita-utils

Version:

Componentes útiles y reutilizables para React con TypeScript

571 lines 31.2 kB
��# =؀� pkg-frontend-utils Una biblioteca completa de utilidades frontend para React que incluye **4 features principales** dise�adas para trabajar juntas y crear formularios din�micos y potentes. ## =��� Features Principales ### 1. <أ� **Hooks Esenciales** (`features/hooks`) - `useSelectOptions` - Manejo de opciones de select - `useFormHandler` - Gesti�n de formularios - `useFormValidation` - Validaci�n en tiempo real - `useDebounce` - Optimizaci�n de performance ### 2. =�� **Sistema de Adaptadores** (`features/adapters`) - Transformaci�n de datos entre diferentes formatos - Adaptadores para APIs, Formik, y m�s - Configuraci�n flexible de transformaciones ### 3. =��� **Sistema CRUD Completo** (`features/crud`) - Operaciones CRUD automatizadas - Tablas din�micas con PrimeReact - Gesti�n de estado de tablas ### 4. =��� **Sistema de Formularios Din�micos** (`features/forms`) - Formularios basados en configuraci�n JSON - Validaciones avanzadas con Yup - Componentes PrimeReact integrados - Arrays anidados y selects dependientes --- ## <د� **Ejemplo Completo: Formulario con Select** Te mostrar� c�mo usar las **4 features juntas** para crear un formulario complejo con selects dependientes. ### =��� **Paso 1: Instalaci�n** ```bash npm install @push-orbita-utils/hooks @push-orbita-utils/adapters @push-orbita-utils/crud @push-orbita-utils/forms ``` ### =��� **Paso 2: Configuraci�n del Formulario** ```typescript // src/components/UserForm.tsx import React from 'react'; import { Formik, Form } from 'formik'; import { DynamicFormFields, FieldConfig } from '@push-orbita-utils/forms'; import { useSelectOptions } from '@push-orbita-utils/hooks'; import { createApiAdapter } from '@push-orbita-utils/adapters'; import { useCrudOperations } from '@push-orbita-utils/crud'; // Configuraci�n del formulario const userFormConfig: FieldConfig[] = [ { name: "nombre", type: "text", label: "Nombre", gridSize: "medium", validation: { required: true, minLength: 2 } }, { name: "email", type: "email", label: "Email", gridSize: "medium", validation: { required: true, email: true } }, { name: "pais", type: "select", label: "Pa�s", selectKey: "paises", gridSize: "medium" }, { name: "departamento", type: "select-dependency", label: "Departamento", dependencyField: "pais", entityName: "departamentosByPais", gridSize: "medium" }, { name: "ciudad", type: "select-dependency", label: "Ciudad", dependencyField: "departamento", entityName: "ciudadesByDepartamento", gridSize: "medium" }, { name: "roles", type: "multiselect", label: "Roles", selectKey: "roles", gridSize: "full" }, { name: "activo", type: "checkbox", label: "Usuario Activo", gridSize: "quarter" } ]; // Componente principal const UserForm = () => { // <أ� Hook 1: useSelectOptions para opciones est�ticas const { options: paisesOptions } = useSelectOptions({ key: 'paises', apiService: () => fetch('/api/paises').then(res => res.json()) }); const { options: rolesOptions } = useSelectOptions({ key: 'roles', apiService: () => fetch('/api/roles').then(res => res.json()) }); // =�� Adapter 2: Crear adaptador para la API const apiAdapter = createApiAdapter({ baseURL: '/api', transformResponse: (data) => data.map((item: any) => ({ value: item.id, label: item.nombre })) }); // =��� CRUD 3: Operaciones CRUD para usuarios const { create: createUser, update: updateUser, isLoading } = useCrudOperations({ entity: 'usuarios', adapter: apiAdapter }); // =��� Forms 4: Manejo del formulario const handleSubmit = async (values: any) => { try { if (values.id) { await updateUser(values); } else { await createUser(values); } console.log('Usuario guardado exitosamente'); } catch (error) { console.error('Error al guardar usuario:', error); } }; return ( <div className="user-form-container"> <h2>Formulario de Usuario</h2> <Formik initialValues={{ nombre: '', email: '', pais: '', departamento: '', ciudad: '', roles: [], activo: false }} onSubmit={handleSubmit} > <DynamicFormFields config={userFormConfig} moduleKey="usuarios" title="Crear/Editar Usuario" onCancel={() => console.log('Cancelado')} t={(key) => key} // Funci�n de traducci�n getLangMessage={(moduleKey, key) => `${moduleKey}.${key}`} useSelectOptions={useSelectOptions} /> </Formik> </div> ); }; export default UserForm; ``` ### =��� **Paso 3: Configuraci�n de APIs** ```typescript // src/services/api.ts export const apiService = { // Obtener pa�ses getPaises: () => fetch('/api/paises').then(res => res.json()), // Obtener departamentos por pa�s getDepartamentosByPais: (paisId: string) => fetch(`/api/departamentos?paisId=${paisId}`).then(res => res.json()), // Obtener ciudades por departamento getCiudadesByDepartamento: (departamentoId: string) => fetch(`/api/ciudades?departamentoId=${departamentoId}`).then(res => res.json()), // Obtener roles getRoles: () => fetch('/api/roles').then(res => res.json()) }; ``` ### =��� **Paso 4: Configuraci�n de Adaptadores** ```typescript // src/config/adapters.ts import { createApiAdapter, createFormikAdapter } from '@push-orbita-utils/adapters'; // Adaptador para API export const apiAdapter = createApiAdapter({ baseURL: '/api', transformResponse: (data) => data.map((item: any) => ({ value: item.id, label: item.nombre })), transformRequest: (data) => ({ ...data, createdAt: new Date().toISOString() }) }); // Adaptador para Formik export const formikAdapter = createFormikAdapter({ transformToForm: (data) => ({ ...data, roles: data.roles?.map((r: any) => r.id) || [] }), transformFromForm: (data) => ({ ...data, roles: data.roles?.map((id: string) => ({ id })) || [] }) }); ``` ### =��� **Paso 5: Configuraci�n de Validaciones** ```typescript // src/config/validations.ts import { commonValidations, fieldTypeValidations } from '@push-orbita-utils/forms'; export const userValidations = { nombre: [ commonValidations.required('El nombre es obligatorio'), commonValidations.minLength(2, 'M�nimo 2 caracteres'), commonValidations.maxLength(50, 'M�ximo 50 caracteres') ], email: [ commonValidations.required('El email es obligatorio'), fieldTypeValidations.email('Email inv�lido') ], pais: [ commonValidations.required('Debe seleccionar un pa�s') ], departamento: [ commonValidations.required('Debe seleccionar un departamento') ], ciudad: [ commonValidations.required('Debe seleccionar una ciudad') ] }; ``` --- ## =�'� **Configuraci�n Avanzada** ### **Selects Dependientes con Cach�** ```typescript // Hook personalizado para selects dependientes const useDependentSelect = (dependencyValue: string, entityName: string) => { const { options, loading, error } = useSelectOptions({ key: `${entityName}_${dependencyValue}`, apiService: () => apiService[entityName](dependencyValue), enabled: !!dependencyValue, cacheTime: 5 * 60 * 1000 // 5 minutos }); return { options, loading, error }; }; ``` ### **Transformaciones de Datos** ```typescript // Transformar datos antes de enviar const transformUserData = (formData: any) => { return { ...formData, nombre: formData.nombre.toUpperCase(), roles: formData.roles.map((role: any) => role.id), fechaCreacion: new Date().toISOString() }; }; ``` ### **Validaci�n en Tiempo Real** ```typescript // Validaci�n personalizada const customValidations = { emailUnico: async (email: string) => { const response = await fetch(`/api/usuarios/check-email?email=${email}`); const { disponible } = await response.json(); return disponible || 'Este email ya est� registrado'; } }; ``` --- ## <ب� **Personalizaci�n de Estilos** ```css /* src/styles/forms.css */ .user-form-container { max-width: 800px; margin: 0 auto; padding: 20px; } .form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; } .form-field { margin-bottom: 15px; } .form-field label { display: block; margin-bottom: 5px; font-weight: 600; color: #333; } .form-field input, .form-field select { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; } .form-field input:focus, .form-field select:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); } .form-buttons { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; } .btn { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background-color 0.2s; } .btn-primary { background-color: #007bff; color: white; } .btn-primary:hover { background-color: #0056b3; } .btn-secondary { background-color: #6c757d; color: white; } .btn-secondary:hover { background-color: #545b62; } ``` --- ## >��� **Testing** ```typescript // src/components/__tests__/UserForm.test.tsx import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { Formik } from 'formik'; import UserForm from '../UserForm'; // Mock de las APIs jest.mock('../../services/api', () => ({ apiService: { getPaises: jest.fn(() => Promise.resolve([ { id: '1', nombre: 'Colombia' }, { id: '2', nombre: 'M�xico' } ])), getDepartamentosByPais: jest.fn(() => Promise.resolve([ { id: '1', nombre: 'Antioquia' }, { id: '2', nombre: 'Cundinamarca' } ])), getRoles: jest.fn(() => Promise.resolve([ { id: '1', nombre: 'Admin' }, { id: '2', nombre: 'Usuario' } ])) } })); describe('UserForm', () => { it('renderiza el formulario correctamente', async () => { render(<UserForm />); expect(screen.getByText('Formulario de Usuario')).toBeInTheDocument(); expect(screen.getByLabelText('Nombre')).toBeInTheDocument(); expect(screen.getByLabelText('Email')).toBeInTheDocument(); expect(screen.getByLabelText('Pa�s')).toBeInTheDocument(); }); it('maneja selects dependientes', async () => { render(<UserForm />); // Seleccionar pa�s const paisSelect = screen.getByLabelText('Pa�s'); fireEvent.change(paisSelect, { target: { value: '1' } }); // Verificar que se cargan los departamentos await waitFor(() => { expect(screen.getByLabelText('Departamento')).toBeInTheDocument(); }); }); it('valida campos requeridos', async () => { render(<UserForm />); const submitButton = screen.getByText('Guardar'); fireEvent.click(submitButton); await waitFor(() => { expect(screen.getByText('El nombre es obligatorio')).toBeInTheDocument(); expect(screen.getByText('El email es obligatorio')).toBeInTheDocument(); }); }); }); ``` --- ## =؀� **Ejecutar el Proyecto** ```bash # Instalar dependencias npm install # Ejecutar en modo desarrollo npm run dev # Ejecutar tests npm test # Construir para producci�n npm run build ``` --- ## =�� **Sistema de Control de Acceso** `push-orbita-utils` incluye un sistema de control de acceso que permite restringir el uso del paquete a usuarios autorizados mediante claves de acceso. Esto te permite mantener el paquete p�blico en npm mientras controlas qui�n puede usarlo. ### **Caracter�sticas del Sistema:** - ' **Control Universal**: Todos los componentes tienen versiones con y sin access control - ' **Validaci�n Autom�tica**: Verificaci�n autom�tica de claves de acceso - ' **Manejo de Errores**: Mensajes claros cuando el acceso es denegado - ' **Flexibilidad**: Configuraci�n personalizable de claves y validaciones ### **Uso B�sico:** ```typescript // Importar utilidades de access control import { setAccessKey, validateAccess, WhatsAppButtonWithAccess } from 'push-orbita-utils'; // Establecer clave de acceso setAccessKey('mauro_test_key'); // Usar componentes con access control const App = () => { return <WhatsAppButtonWithAccess phoneNumber="123456789" />; }; ``` ### **=��� [Documentaci�n Completa del Sistema de Control de Acceso](./docs/ACCESS_CONTROL_COMPLETE.md)** **�&� IMPORTANTE**: Al finalizar cada feature, SIEMPRE: 1. **Implementar el sistema de control de acceso** 2. **Crear versi�n segura del componente/hook/adaptador** 3. **Verificar que todas las exportaciones est�n correctas** 4. **Ejecutar `npm run build` para confirmar que no hay errores** 5. **Probar las importaciones en un proyecto de ejemplo** --- ## =��� **Documentaci�n Adicional** - [=��� Documentaci�n de Hooks](./src/features/hooks/docs/README.md) - [=�� Documentaci�n de Adaptadores](./src/features/adapters/docs/README.md) - [=��� Documentaci�n de CRUD](./src/features/crud/docs/README.md) - [=��� Documentaci�n de Formularios](./src/features/forms/docs/README.md) --- ## >�� **Contribuci�n** 1. Fork el repositorio 2. Crea una rama para tu feature (`git checkout -b feature/AmazingFeature`) 3. Commit tus cambios (`git commit -m 'Add some AmazingFeature'`) 4. Push a la rama (`git push origin feature/AmazingFeature`) 5. Abre un Pull Request --- ## =��� **Licencia** Este proyecto est� bajo la licencia MIT. Ver el archivo [LICENSE](LICENSE) para m�s detalles. --- ## <ؘ� **Soporte** Si tienes alguna pregunta o necesitas ayuda: - =��� Email: soporte@push-orbita.com - =ج� Discord: [Canal de Soporte](https://discord.gg/push-orbita) - =��� Documentaci�n: [docs.push-orbita.com](https://docs.push-orbita.com) --- **�Disfruta creando formularios din�micos y potentes con nuestras 4 features integradas! <؉�**