UNPKG

@pagamio/frontend-commons-lib

Version:

Pagamio library for Frontend reusable components like the form engine and table container

753 lines (588 loc) 18.3 kB
# pagamio-frontend-commons-library A reusable React component library styled with Tailwind CSS, designed to streamline your frontend development process. ## :rocket: **Features** - **A Form Engine**: A package with all possible input components that can be used to build a form. This helps render forms in your project. - **Reusable Components**: A collection of customizable components like DateInput, TextInput, and more. - **Tailwind CSS**: Utility-first CSS framework for rapid UI development. - **TypeScript Support**: Fully typed components for enhanced developer experience. - **Flexible Imports**: Import components individually or collectively. - **Peer Dependencies**: Ensures compatibility with React and React DOM versions. - **API Client**: Robust API integration with authentication support, SWR data fetching, and mocking capabilities. ## :package: **Installation** Install the library via Yarn: ```bash yarn add pagamio-frontend-commons-lib ``` ## :art: Usage - **Importing Styles** Import the compiled Tailwind CSS styles into your application's entry point (e.g., index.js or App.js): `import 'pagamio-frontend-commons-lib/lib/styles.css';` - **Importing Components** You can import components individually or collectively. - a. Named Imports Import multiple components from the main entry point: `import { DateInput, TextInput } from 'pagamio-frontend-commons-lib';` ### :gear: Environment Variables Some utilities (for example the `useImageUpload` hook and `ImageUploader` component) require an API endpoint that issues presigned upload URLs. Make sure your host application exposes the following public environment variable before using those helpers: | Variable | Description | | --- | --- | | `NEXT_PUBLIC_UPLOAD_URL_ENDPOINT` | HTTP endpoint that returns presigned URLs for file uploads. | ```bash # .env (per app) NEXT_PUBLIC_UPLOAD_URL_ENDPOINT=https://<your-upload-service>/upload-url ``` This value is resolved at runtime; ## :shield: **RBAC Module** The Role-Based Access Control (RBAC) module provides a flexible system for implementing permission-based access control in your applications. ### **Features** - Generic TypeScript implementation that works with any permission system - Flexible configuration that can be initialized once at application startup - Pure utility functions that can be used anywhere in your application - React hooks for convenient use in components - Support for role-based and permission-based access control - Type-safe API with generics for custom user and permission types ### **Core Components** #### **Initialization** Initialize the RBAC system with your application-specific configuration: ```typescript import { initializeRBAC } from 'pagamio-frontend-commons-lib/rbac'; import { Permissions } from './permissions'; // Define your permission enum enum Permissions { ALL = 'ALL', VIEW_DASHBOARD = 'VIEW_DASHBOARD', MANAGE_USERS = 'MANAGE_USERS', // ... other permissions } // Define your RBAC configuration const rbacConfig = { 'ADMIN': [Permissions.ALL], 'MANAGER': [Permissions.VIEW_DASHBOARD, Permissions.MANAGE_USERS], 'USER': [Permissions.VIEW_DASHBOARD], }; // Initialize RBAC with your configuration initializeRBAC({ rbacConfig, allPermissionValue: Permissions.ALL, allPermissions: Object.values(Permissions), roleKey: 'roleName' // The property in your user object that contains the role }); ``` #### **Utility Functions** Use the RBAC utility functions to check permissions and roles: ```typescript import { hasPermission, hasRole, getUserPermissions } from 'pagamio-frontend-commons-lib/rbac'; import { Permissions } from './permissions'; // Check if a user has a specific permission const canViewDashboard = hasPermission(user, Permissions.VIEW_DASHBOARD); // Check if a user has a specific role const isAdmin = hasRole(user, 'ADMIN'); // Get all permissions for a user const userPermissions = getUserPermissions(user); ``` #### **React Hooks** Use the RBAC hooks in your React components: ```typescript import { useHasPermission, useHasRole } from 'pagamio-frontend-commons-lib/rbac'; import { useAuth } from 'pagamio-frontend-commons-lib/auth'; import { Permissions } from './permissions'; function Dashboard() { const { user } = useAuth(); const canManageUsers = useHasPermission(user, Permissions.MANAGE_USERS); return ( <div> <h1>Dashboard</h1> {canManageUsers && <UserManagement />} </div> ); } ``` ## :globe_with_meridians: **API Module** The API module provides a robust system for making API requests with built-in authentication, caching, and error handling capabilities. ### **Features** - TypeScript support with generic types for type-safe API operations - Authentication integration with token management - SWR integration for data fetching, caching, and revalidation - Support for RESTful operations (GET, POST, PUT, PATCH, DELETE) - Pagination support for large datasets - Configurable retry logic and timeout handling - Request and response interceptors - Error handling and logging - Mock API support for testing and development ### **Core Components** #### **ApiClient** The `ApiClient` class provides a flexible HTTP client with authentication support, retry logic, and error handling. ```typescript import { ApiClient, createApiClient } from 'pagamio-frontend-commons-lib'; // Create a client with your custom auth configuration const apiClient = createApiClient<MyAuthConfig>({ baseURL: 'https://api.example.com', tokenManager: tokenManager, defaultHeaders: { 'Content-Type': 'application/json', }, timeout: 5000, retries: 2, }); // Making API calls const data = await apiClient.get<MyResponseType>('/users'); const newUser = await apiClient.post<UserResponse>('/users', { name: 'John', email: 'john@example.com' }); ``` #### **SWR Integration** The API module includes SWR hooks for efficient data fetching with caching, revalidation, and focus refetching. ```typescript import { useApiSWR, usePaginatedApiSWR } from 'pagamio-frontend-commons-lib'; // Basic data fetching with SWR function UserProfile({ userId }) { const { data, error, isLoading } = useApiSWR<UserData>(`/users/${userId}`); if (isLoading) return <div>Loading ... </div>; if (error) return <div>Error loading user data < /div>; return <div>Hello, { data.name }! < /div>; } // Paginated data fetching function UserList() { const { data, error, isLoading } = usePaginatedApiSWR<User>('/users', { params: { page: 0, size: 10 } }); if (isLoading) return <div>Loading users ... </div>; return ( <div> <h2>Users({ data.totalElements }) < /h2> < ul > { data.content.map(user => <li key = { user.id } > { user.name } < /li>)} < /ul> < /div> ); } ``` #### **API Context Provider** The `ApiProvider` component makes the API client available throughout your application. ```typescript import { ApiProvider, createApiClient } from 'pagamio-frontend-commons-lib'; // Create API client const apiClient = createApiClient<MyAuthConfig>({ baseURL: 'https://api.example.com', tokenManager: tokenManager, }); // Wrap your application with the provider function App() { return ( <ApiProvider apiClient = { apiClient } > <YourApplication / > </ApiProvider> ); } ``` #### **API Mutations** The API module provides hooks for performing mutations (create, update, delete operations). ```typescript import { useApiMutation, useApiSWRWithMutation } from 'pagamio-frontend-commons-lib'; // Using the mutation hook function CreateUserForm() { const mutation = useApiMutation<UserResponse>(); const handleSubmit = async (userData) => { try { const newUser = await mutation.post('/users', userData); alert(`User ${newUser.name} created successfully!`); } catch (error) { console.error('Failed to create user:', error); } }; return <form onSubmit = { handleSubmit } > {/* form fields */ } < /form>; } // Combined SWR and mutation hook function EditUserProfile({ userId }) { const { data, error, isLoading, mutate } = useApiSWRWithMutation<UserData>( `/users/${userId}` ); const handleUpdate = async (updatedData) => { try { await mutate.patch(`/users/${userId}`, updatedData); alert('Profile updated successfully!'); } catch (error) { console.error('Failed to update profile:', error); } }; if (isLoading) return <div>Loading ... </div>; return <ProfileForm user = { data } onSubmit = { handleUpdate } />; } ``` ### **Mocking API Requests** The API module supports mocking API requests for testing and development. ```typescript import { ApiProvider, createApiClient, MockConfig } from 'pagamio-frontend-commons-lib'; // Define mock configurations const mockConfig: MockConfig[] = [ { path: '/users', method: 'GET', response: [ { id: 1, name: 'John Doe', email: 'john@example.com' }, { id: 2, name: 'Jane Smith', email: 'jane@example.com' } ] }, { path: '/users', method: 'POST', params: { name: 'Alice', email: 'alice@example.com' }, response: { id: 3, name: 'Alice', email: 'alice@example.com' } } ]; // Enable mocking in the provider function TestApp() { return ( <ApiProvider apiClient = { apiClient } mocked = { true } mockConfig = { mockConfig } > <YourApplication / > </ApiProvider> ) ; } ``` ### **Authentication Integration** The API client integrates with the token manager for authentication. ```typescript import { createApiClient, createTokenManager } from 'pagamio-frontend-commons-lib'; // Create a token manager const tokenManager = createTokenManager<MyAuthConfig>({ baseUrl: 'https://api.example.com', refreshEndpoint: '/auth/refresh', cookieOptions: { secure: true, sameSite: 'strict' } }); // Create API client with authentication const apiClient = createApiClient<MyAuthConfig>({ baseURL: 'https://api.example.com', tokenManager: tokenManager, onUnauthorized: () => { // Handle unauthorized access (e.g., redirect to login) window.location.href = '/login'; } }); ``` ### **Advanced Configuration** #### **Custom Request/Response Handling** ```typescript const apiClient = createApiClient<MyAuthConfig>({ baseURL: 'https://api.example.com', tokenManager: tokenManager, onRequest: async (config) => { // Add custom headers or modify request configuration config.headers = { ...config.headers, 'X-Custom-Header': 'CustomValue' }; return config; }, onResponse: async (response, data) => { // Transform or process response data return data.results || data; }, onError: async (error) => { // Log or handle errors console.error(`API Error (${error.status}):`, error.message); } }); ``` #### **Pagination and Filtering** ```typescript // Custom pagination params const { data } = useApiSWR<UserListResponse>('/users', { params: { page: 0, size: 25, sortBy: 'createdAt', sortDir: 'desc', name: 'John' } }); ``` ### **TypeScript Integration** The API module is fully typed with TypeScript, providing type safety and better developer experience. ```typescript // Define your auth configuration interface MyAuthConfig extends CustomAuthConfig { UserInfo: { id: string; username: string; email: string; roles: string[]; }; TokenInfo: { token: string; expiresIn: number; }; Credentials: { username: string; password: string; }; } // Use your custom types with the API client const apiClient = createApiClient<MyAuthConfig>({ baseURL: 'https://api.example.com', tokenManager: tokenManager }); // Type-safe API calls interface Product { id: string; name: string; price: number; } const { data: products } = useApiSWR<Product[]>('/products'); ``` ### **Best Practices** 1. **Centralize API Configuration**: Create a central API configuration file that sets up the API client and exports hooks for use throughout your application. 2. **Use TypeScript**: Leverage TypeScript definitions for type-safe API calls and better developer experience. 3. **Handle Loading and Error States**: Always handle loading and error states in your components when using API hooks. 4. **Implement Proper Error Handling**: Configure error handling with the `onError` callback and handle errors appropriately in your UI. 5. **Use Mocking for Development**: Enable mocking during development to work without a backend or to test specific scenarios. 6. **Optimize Cache Invalidation**: Use SWR's `mutate` function to keep your data fresh and update the UI after mutations. 7. **Set Appropriate Timeouts**: Configure request timeouts based on the expected response time of your API endpoints. ## :globe_with_meridians: **Translations** The library provides a complete translation system that supports internationalization for your applications. ### **Languages Currently Supported** The library comes with built-in support for the following languages: - English (en) - Spanish (es) - French (fr) - Portuguese (pt) ### **Implementation Guide** Follow these steps to implement translations in your project: #### 1. **Setup Translation Provider** Wrap your application with the `TranslationProvider`: ```tsx import { TranslationProvider } from 'pagamio-frontend-commons-lib'; function App() { return ( <TranslationProvider defaultLocale="en" loadPath="/translations" // Path to your translation files > <YourApplicationComponents /> </TranslationProvider> ); } ``` Alternatively, if you're using the `AppLayout` component: ```tsx import { AppLayout } from 'pagamio-frontend-commons-lib'; function App() { return ( <AppLayout // other props... enableTranslations={true} translationConfig={{ defaultLocale: 'en', loadPath: '/translations', }} > {/* Your content */} </AppLayout> ); } ``` #### 2. **Create Translation Files** Create JSON files for each language in your project: ``` /public /translations en.json es.json fr.json pt.json ``` Example of a translation file (`en.json`): ```json { "common": { "save": "Save", "cancel": "Cancel", "submit": "Submit" }, "auth": { "login": "Login", "register": "Register", "forgotPassword": "Forgot Password?" } } ``` #### 3. **Add i18next-scanner Config** Create an `i18next-scanner.config.js` in your project root: ```js module.exports = { input: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.test.{js,jsx,ts,tsx}', '!**/node_modules/**'], output: './public/translations/', options: { debug: true, func: { list: ['t', 'tLib'], extensions: ['.js', '.jsx', '.ts', '.tsx'], }, lngs: ['en', 'es', 'fr', 'pt'], defaultLng: 'en', defaultValue: function(lng, ns, key) { return key; }, resource: { loadPath: 'public/translations/{{lng}}.json', savePath: '{{lng}}.json', jsonIndent: 2, lineEnding: '\n', }, removeUnusedKeys: false, nsSeparator: false, keySeparator: '.', }, }; ``` #### 4. **Add Extraction Script to package.json** ```json "scripts": { "extract-translations": "i18next-scanner --config i18next-scanner.config.js 'src/**/*.{js,jsx,ts,tsx}'" } ``` #### 5. **Install Required Dependencies** Install the necessary dev dependencies: ```bash yarn add -D i18next-scanner ``` Or if you're using npm: ```bash npm install --save-dev i18next-scanner ``` #### 6. **Use Translations in Components** ```tsx import { useTranslation } from 'pagamio-frontend-commons-lib'; function YourComponent() { const { t } = useTranslation(); return ( <div> <h1>{t('common.title', 'Default Title')}</h1> <p>{t('common.description', 'This is a default description')}</p> <button>{t('common.save', 'Save')}</button> </div> ); } ``` #### 7. **Use Library Translations** The library provides common translations for frequently used UI elements: ```tsx import { useLibTranslations } from 'pagamio-frontend-commons-lib'; function YourComponent() { const { tLib } = useLibTranslations(); return ( <div> <button>{tLib('save')}</button> <button>{tLib('cancel')}</button> <button>{tLib('submit')}</button> </div> ); } ``` #### 8. **Add Locale Switcher** Add a language switcher to allow users to change the application language: ```tsx import { LocaleSwitcher } from 'pagamio-frontend-commons-lib'; function Header() { return ( <header> <nav> {/* Your other navigation elements */} <LocaleSwitcher /> </nav> </header> ); } ``` You can customize language display names: ```tsx <LocaleSwitcher localeNames={{ en: 'English', es: 'Español', fr: 'Français', pt: 'Português' }} /> ``` #### 9. **Run the Extraction Script** Extract all translation keys from your application: ```bash yarn extract-translations ``` This will scan your code for translation keys and update your translation files. ### **Advanced Configuration** #### **Custom Locale Detector** You can configure the translation system to detect the browser's locale: ```tsx import { TranslationProvider, detectBrowserLocale } from 'pagamio-frontend-commons-lib'; function App() { const browserLocale = detectBrowserLocale(); // Returns 'en', 'es', etc. return ( <TranslationProvider defaultLocale={browserLocale} loadPath="/translations" > <YourApplicationComponents /> </TranslationProvider> ); } ``` #### **Direct Loading of Translation Data** Instead of loading translations from files, you can provide them directly: ```tsx import { TranslationProvider } from 'pagamio-frontend-commons-lib'; const translations = [ { locale: 'en', messages: { common: { save: 'Save', cancel: 'Cancel' } } }, { locale: 'es', messages: { common: { save: 'Guardar', cancel: 'Cancelar' } } } ]; function App() { return ( <TranslationProvider defaultLocale="en" localeData={translations} > <YourApplicationComponents /> </TranslationProvider> ); } ```