pagamio-frontend-commons-lib
Version:
Pagamio library for Frontend reusable components like the form engine and table container
738 lines (578 loc) • 17.8 kB
Markdown
# 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';`
## :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>
);
}
```