@pagamio/frontend-commons-lib 0.8.175
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +737 -0
- package/package.json +180 -0
package/README.md
ADDED
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
# pagamio-frontend-commons-library
|
|
2
|
+
|
|
3
|
+
A reusable React component library styled with Tailwind CSS, designed to streamline your frontend development process.
|
|
4
|
+
|
|
5
|
+
## :rocket: **Features**
|
|
6
|
+
|
|
7
|
+
- **A Form Engine**: A package with all possible input components that can be used to build a form. This helps render
|
|
8
|
+
forms in your project.
|
|
9
|
+
- **Reusable Components**: A collection of customizable components like DateInput, TextInput, and more.
|
|
10
|
+
- **Tailwind CSS**: Utility-first CSS framework for rapid UI development.
|
|
11
|
+
- **TypeScript Support**: Fully typed components for enhanced developer experience.
|
|
12
|
+
- **Flexible Imports**: Import components individually or collectively.
|
|
13
|
+
- **Peer Dependencies**: Ensures compatibility with React and React DOM versions.
|
|
14
|
+
- **API Client**: Robust API integration with authentication support, SWR data fetching, and mocking capabilities.
|
|
15
|
+
|
|
16
|
+
## :package: **Installation**
|
|
17
|
+
|
|
18
|
+
Install the library via Yarn:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
yarn add pagamio-frontend-commons-lib
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## :art: Usage
|
|
25
|
+
|
|
26
|
+
- **Importing Styles**
|
|
27
|
+
|
|
28
|
+
Import the compiled Tailwind CSS styles into your application's entry point (e.g., index.js or App.js):
|
|
29
|
+
|
|
30
|
+
`import 'pagamio-frontend-commons-lib/lib/styles.css';`
|
|
31
|
+
|
|
32
|
+
- **Importing Components**
|
|
33
|
+
|
|
34
|
+
You can import components individually or collectively.
|
|
35
|
+
|
|
36
|
+
- a. Named Imports
|
|
37
|
+
|
|
38
|
+
Import multiple components from the main entry point:
|
|
39
|
+
|
|
40
|
+
`import { DateInput, TextInput } from 'pagamio-frontend-commons-lib';`
|
|
41
|
+
|
|
42
|
+
## :shield: **RBAC Module**
|
|
43
|
+
|
|
44
|
+
The Role-Based Access Control (RBAC) module provides a flexible system for implementing permission-based access control in your applications.
|
|
45
|
+
|
|
46
|
+
### **Features**
|
|
47
|
+
|
|
48
|
+
- Generic TypeScript implementation that works with any permission system
|
|
49
|
+
- Flexible configuration that can be initialized once at application startup
|
|
50
|
+
- Pure utility functions that can be used anywhere in your application
|
|
51
|
+
- React hooks for convenient use in components
|
|
52
|
+
- Support for role-based and permission-based access control
|
|
53
|
+
- Type-safe API with generics for custom user and permission types
|
|
54
|
+
|
|
55
|
+
### **Core Components**
|
|
56
|
+
|
|
57
|
+
#### **Initialization**
|
|
58
|
+
|
|
59
|
+
Initialize the RBAC system with your application-specific configuration:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { initializeRBAC } from 'pagamio-frontend-commons-lib/rbac';
|
|
63
|
+
import { Permissions } from './permissions';
|
|
64
|
+
|
|
65
|
+
// Define your permission enum
|
|
66
|
+
enum Permissions {
|
|
67
|
+
ALL = 'ALL',
|
|
68
|
+
VIEW_DASHBOARD = 'VIEW_DASHBOARD',
|
|
69
|
+
MANAGE_USERS = 'MANAGE_USERS',
|
|
70
|
+
// ... other permissions
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Define your RBAC configuration
|
|
74
|
+
const rbacConfig = {
|
|
75
|
+
'ADMIN': [Permissions.ALL],
|
|
76
|
+
'MANAGER': [Permissions.VIEW_DASHBOARD, Permissions.MANAGE_USERS],
|
|
77
|
+
'USER': [Permissions.VIEW_DASHBOARD],
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Initialize RBAC with your configuration
|
|
81
|
+
initializeRBAC({
|
|
82
|
+
rbacConfig,
|
|
83
|
+
allPermissionValue: Permissions.ALL,
|
|
84
|
+
allPermissions: Object.values(Permissions),
|
|
85
|
+
roleKey: 'roleName' // The property in your user object that contains the role
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### **Utility Functions**
|
|
90
|
+
|
|
91
|
+
Use the RBAC utility functions to check permissions and roles:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { hasPermission, hasRole, getUserPermissions } from 'pagamio-frontend-commons-lib/rbac';
|
|
95
|
+
import { Permissions } from './permissions';
|
|
96
|
+
|
|
97
|
+
// Check if a user has a specific permission
|
|
98
|
+
const canViewDashboard = hasPermission(user, Permissions.VIEW_DASHBOARD);
|
|
99
|
+
|
|
100
|
+
// Check if a user has a specific role
|
|
101
|
+
const isAdmin = hasRole(user, 'ADMIN');
|
|
102
|
+
|
|
103
|
+
// Get all permissions for a user
|
|
104
|
+
const userPermissions = getUserPermissions(user);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### **React Hooks**
|
|
108
|
+
|
|
109
|
+
Use the RBAC hooks in your React components:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { useHasPermission, useHasRole } from 'pagamio-frontend-commons-lib/rbac';
|
|
113
|
+
import { useAuth } from 'pagamio-frontend-commons-lib/auth';
|
|
114
|
+
import { Permissions } from './permissions';
|
|
115
|
+
|
|
116
|
+
function Dashboard() {
|
|
117
|
+
const { user } = useAuth();
|
|
118
|
+
const canManageUsers = useHasPermission(user, Permissions.MANAGE_USERS);
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<div>
|
|
122
|
+
<h1>Dashboard</h1>
|
|
123
|
+
{canManageUsers && <UserManagement />}
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## :globe_with_meridians: **API Module**
|
|
130
|
+
|
|
131
|
+
The API module provides a robust system for making API requests with built-in authentication, caching, and error
|
|
132
|
+
handling capabilities.
|
|
133
|
+
|
|
134
|
+
### **Features**
|
|
135
|
+
|
|
136
|
+
- TypeScript support with generic types for type-safe API operations
|
|
137
|
+
- Authentication integration with token management
|
|
138
|
+
- SWR integration for data fetching, caching, and revalidation
|
|
139
|
+
- Support for RESTful operations (GET, POST, PUT, PATCH, DELETE)
|
|
140
|
+
- Pagination support for large datasets
|
|
141
|
+
- Configurable retry logic and timeout handling
|
|
142
|
+
- Request and response interceptors
|
|
143
|
+
- Error handling and logging
|
|
144
|
+
- Mock API support for testing and development
|
|
145
|
+
|
|
146
|
+
### **Core Components**
|
|
147
|
+
|
|
148
|
+
#### **ApiClient**
|
|
149
|
+
|
|
150
|
+
The `ApiClient` class provides a flexible HTTP client with authentication support, retry logic, and error handling.
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { ApiClient, createApiClient } from 'pagamio-frontend-commons-lib';
|
|
154
|
+
|
|
155
|
+
// Create a client with your custom auth configuration
|
|
156
|
+
const apiClient = createApiClient<MyAuthConfig>({
|
|
157
|
+
baseURL: 'https://api.example.com',
|
|
158
|
+
tokenManager: tokenManager,
|
|
159
|
+
defaultHeaders: {
|
|
160
|
+
'Content-Type': 'application/json',
|
|
161
|
+
},
|
|
162
|
+
timeout: 5000,
|
|
163
|
+
retries: 2,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Making API calls
|
|
167
|
+
const data = await apiClient.get<MyResponseType>('/users');
|
|
168
|
+
const newUser = await apiClient.post<UserResponse>('/users', { name: 'John', email: 'john@example.com' });
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### **SWR Integration**
|
|
172
|
+
|
|
173
|
+
The API module includes SWR hooks for efficient data fetching with caching, revalidation, and focus refetching.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { useApiSWR, usePaginatedApiSWR } from 'pagamio-frontend-commons-lib';
|
|
177
|
+
|
|
178
|
+
// Basic data fetching with SWR
|
|
179
|
+
function UserProfile({ userId }) {
|
|
180
|
+
const { data, error, isLoading } = useApiSWR<UserData>(`/users/${userId}`);
|
|
181
|
+
|
|
182
|
+
if (isLoading) return <div>Loading
|
|
183
|
+
...
|
|
184
|
+
</div>;
|
|
185
|
+
if (error) return <div>Error
|
|
186
|
+
loading
|
|
187
|
+
user
|
|
188
|
+
data < /div>;
|
|
189
|
+
|
|
190
|
+
return <div>Hello, { data.name }! < /div>;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Paginated data fetching
|
|
194
|
+
function UserList() {
|
|
195
|
+
const { data, error, isLoading } = usePaginatedApiSWR<User>('/users', {
|
|
196
|
+
params: { page: 0, size: 10 }
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (isLoading) return <div>Loading
|
|
200
|
+
users
|
|
201
|
+
...
|
|
202
|
+
</div>;
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<div>
|
|
206
|
+
<h2>Users({ data.totalElements }) < /h2>
|
|
207
|
+
< ul >
|
|
208
|
+
{
|
|
209
|
+
data.content.map(user => <li key = { user.id } > { user.name } < /li>)}
|
|
210
|
+
< /ul>
|
|
211
|
+
< /div>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### **API Context Provider**
|
|
217
|
+
|
|
218
|
+
The `ApiProvider` component makes the API client available throughout your application.
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { ApiProvider, createApiClient } from 'pagamio-frontend-commons-lib';
|
|
222
|
+
|
|
223
|
+
// Create API client
|
|
224
|
+
const apiClient = createApiClient<MyAuthConfig>({
|
|
225
|
+
baseURL: 'https://api.example.com',
|
|
226
|
+
tokenManager: tokenManager,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Wrap your application with the provider
|
|
230
|
+
function App() {
|
|
231
|
+
return (
|
|
232
|
+
<ApiProvider apiClient = { apiClient } >
|
|
233
|
+
<YourApplication / >
|
|
234
|
+
</ApiProvider>
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
#### **API Mutations**
|
|
240
|
+
|
|
241
|
+
The API module provides hooks for performing mutations (create, update, delete operations).
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { useApiMutation, useApiSWRWithMutation } from 'pagamio-frontend-commons-lib';
|
|
245
|
+
|
|
246
|
+
// Using the mutation hook
|
|
247
|
+
function CreateUserForm() {
|
|
248
|
+
const mutation = useApiMutation<UserResponse>();
|
|
249
|
+
|
|
250
|
+
const handleSubmit = async (userData) => {
|
|
251
|
+
try {
|
|
252
|
+
const newUser = await mutation.post('/users', userData);
|
|
253
|
+
alert(`User ${newUser.name} created successfully!`);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
console.error('Failed to create user:', error);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
return <form onSubmit = { handleSubmit } > {/* form fields */ } < /form>;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Combined SWR and mutation hook
|
|
263
|
+
function EditUserProfile({ userId }) {
|
|
264
|
+
const { data, error, isLoading, mutate } = useApiSWRWithMutation<UserData>(
|
|
265
|
+
`/users/${userId}`
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const handleUpdate = async (updatedData) => {
|
|
269
|
+
try {
|
|
270
|
+
await mutate.patch(`/users/${userId}`, updatedData);
|
|
271
|
+
alert('Profile updated successfully!');
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.error('Failed to update profile:', error);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
if (isLoading) return <div>Loading
|
|
278
|
+
...
|
|
279
|
+
</div>;
|
|
280
|
+
|
|
281
|
+
return <ProfileForm user = { data }
|
|
282
|
+
onSubmit = { handleUpdate }
|
|
283
|
+
/>;
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### **Mocking API Requests**
|
|
288
|
+
|
|
289
|
+
The API module supports mocking API requests for testing and development.
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { ApiProvider, createApiClient, MockConfig } from 'pagamio-frontend-commons-lib';
|
|
293
|
+
|
|
294
|
+
// Define mock configurations
|
|
295
|
+
const mockConfig: MockConfig[] = [
|
|
296
|
+
{
|
|
297
|
+
path: '/users',
|
|
298
|
+
method: 'GET',
|
|
299
|
+
response: [
|
|
300
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com' },
|
|
301
|
+
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
|
|
302
|
+
]
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
path: '/users',
|
|
306
|
+
method: 'POST',
|
|
307
|
+
params: { name: 'Alice', email: 'alice@example.com' },
|
|
308
|
+
response: { id: 3, name: 'Alice', email: 'alice@example.com' }
|
|
309
|
+
}
|
|
310
|
+
];
|
|
311
|
+
|
|
312
|
+
// Enable mocking in the provider
|
|
313
|
+
function TestApp() {
|
|
314
|
+
return (
|
|
315
|
+
<ApiProvider
|
|
316
|
+
apiClient = { apiClient }
|
|
317
|
+
mocked = { true }
|
|
318
|
+
mockConfig = { mockConfig } >
|
|
319
|
+
<YourApplication / >
|
|
320
|
+
</ApiProvider>
|
|
321
|
+
)
|
|
322
|
+
;
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### **Authentication Integration**
|
|
327
|
+
|
|
328
|
+
The API client integrates with the token manager for authentication.
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { createApiClient, createTokenManager } from 'pagamio-frontend-commons-lib';
|
|
332
|
+
|
|
333
|
+
// Create a token manager
|
|
334
|
+
const tokenManager = createTokenManager<MyAuthConfig>({
|
|
335
|
+
baseUrl: 'https://api.example.com',
|
|
336
|
+
refreshEndpoint: '/auth/refresh',
|
|
337
|
+
cookieOptions: {
|
|
338
|
+
secure: true,
|
|
339
|
+
sameSite: 'strict'
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Create API client with authentication
|
|
344
|
+
const apiClient = createApiClient<MyAuthConfig>({
|
|
345
|
+
baseURL: 'https://api.example.com',
|
|
346
|
+
tokenManager: tokenManager,
|
|
347
|
+
onUnauthorized: () => {
|
|
348
|
+
// Handle unauthorized access (e.g., redirect to login)
|
|
349
|
+
window.location.href = '/login';
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### **Advanced Configuration**
|
|
355
|
+
|
|
356
|
+
#### **Custom Request/Response Handling**
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
const apiClient = createApiClient<MyAuthConfig>({
|
|
360
|
+
baseURL: 'https://api.example.com',
|
|
361
|
+
tokenManager: tokenManager,
|
|
362
|
+
onRequest: async (config) => {
|
|
363
|
+
// Add custom headers or modify request configuration
|
|
364
|
+
config.headers = {
|
|
365
|
+
...config.headers,
|
|
366
|
+
'X-Custom-Header': 'CustomValue'
|
|
367
|
+
};
|
|
368
|
+
return config;
|
|
369
|
+
},
|
|
370
|
+
onResponse: async (response, data) => {
|
|
371
|
+
// Transform or process response data
|
|
372
|
+
return data.results || data;
|
|
373
|
+
},
|
|
374
|
+
onError: async (error) => {
|
|
375
|
+
// Log or handle errors
|
|
376
|
+
console.error(`API Error (${error.status}):`, error.message);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
#### **Pagination and Filtering**
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
// Custom pagination params
|
|
385
|
+
const { data } = useApiSWR<UserListResponse>('/users', {
|
|
386
|
+
params: {
|
|
387
|
+
page: 0,
|
|
388
|
+
size: 25,
|
|
389
|
+
sortBy: 'createdAt',
|
|
390
|
+
sortDir: 'desc',
|
|
391
|
+
name: 'John'
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### **TypeScript Integration**
|
|
397
|
+
|
|
398
|
+
The API module is fully typed with TypeScript, providing type safety and better developer experience.
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
// Define your auth configuration
|
|
402
|
+
interface MyAuthConfig extends CustomAuthConfig {
|
|
403
|
+
UserInfo: {
|
|
404
|
+
id: string;
|
|
405
|
+
username: string;
|
|
406
|
+
email: string;
|
|
407
|
+
roles: string[];
|
|
408
|
+
};
|
|
409
|
+
TokenInfo: {
|
|
410
|
+
token: string;
|
|
411
|
+
expiresIn: number;
|
|
412
|
+
};
|
|
413
|
+
Credentials: {
|
|
414
|
+
username: string;
|
|
415
|
+
password: string;
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Use your custom types with the API client
|
|
420
|
+
const apiClient = createApiClient<MyAuthConfig>({
|
|
421
|
+
baseURL: 'https://api.example.com',
|
|
422
|
+
tokenManager: tokenManager
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// Type-safe API calls
|
|
426
|
+
interface Product {
|
|
427
|
+
id: string;
|
|
428
|
+
name: string;
|
|
429
|
+
price: number;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const { data: products } = useApiSWR<Product[]>('/products');
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### **Best Practices**
|
|
436
|
+
|
|
437
|
+
1. **Centralize API Configuration**: Create a central API configuration file that sets up the API client and exports
|
|
438
|
+
hooks for use throughout your application.
|
|
439
|
+
|
|
440
|
+
2. **Use TypeScript**: Leverage TypeScript definitions for type-safe API calls and better developer experience.
|
|
441
|
+
|
|
442
|
+
3. **Handle Loading and Error States**: Always handle loading and error states in your components when using API hooks.
|
|
443
|
+
|
|
444
|
+
4. **Implement Proper Error Handling**: Configure error handling with the `onError` callback and handle errors
|
|
445
|
+
appropriately in your UI.
|
|
446
|
+
|
|
447
|
+
5. **Use Mocking for Development**: Enable mocking during development to work without a backend or to test specific
|
|
448
|
+
scenarios.
|
|
449
|
+
|
|
450
|
+
6. **Optimize Cache Invalidation**: Use SWR's `mutate` function to keep your data fresh and update the UI after
|
|
451
|
+
mutations.
|
|
452
|
+
|
|
453
|
+
7. **Set Appropriate Timeouts**: Configure request timeouts based on the expected response time of your API endpoints.
|
|
454
|
+
|
|
455
|
+
## :globe_with_meridians: **Translations**
|
|
456
|
+
|
|
457
|
+
The library provides a complete translation system that supports internationalization for your applications.
|
|
458
|
+
|
|
459
|
+
### **Languages Currently Supported**
|
|
460
|
+
|
|
461
|
+
The library comes with built-in support for the following languages:
|
|
462
|
+
|
|
463
|
+
- English (en)
|
|
464
|
+
- Spanish (es)
|
|
465
|
+
- French (fr)
|
|
466
|
+
- Portuguese (pt)
|
|
467
|
+
|
|
468
|
+
### **Implementation Guide**
|
|
469
|
+
|
|
470
|
+
Follow these steps to implement translations in your project:
|
|
471
|
+
|
|
472
|
+
#### 1. **Setup Translation Provider**
|
|
473
|
+
|
|
474
|
+
Wrap your application with the `TranslationProvider`:
|
|
475
|
+
|
|
476
|
+
```tsx
|
|
477
|
+
import { TranslationProvider } from 'pagamio-frontend-commons-lib';
|
|
478
|
+
|
|
479
|
+
function App() {
|
|
480
|
+
return (
|
|
481
|
+
<TranslationProvider
|
|
482
|
+
defaultLocale="en"
|
|
483
|
+
loadPath="/translations" // Path to your translation files
|
|
484
|
+
>
|
|
485
|
+
<YourApplicationComponents />
|
|
486
|
+
</TranslationProvider>
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Alternatively, if you're using the `AppLayout` component:
|
|
492
|
+
|
|
493
|
+
```tsx
|
|
494
|
+
import { AppLayout } from 'pagamio-frontend-commons-lib';
|
|
495
|
+
|
|
496
|
+
function App() {
|
|
497
|
+
return (
|
|
498
|
+
<AppLayout
|
|
499
|
+
// other props...
|
|
500
|
+
enableTranslations={true}
|
|
501
|
+
translationConfig={{
|
|
502
|
+
defaultLocale: 'en',
|
|
503
|
+
loadPath: '/translations',
|
|
504
|
+
}}
|
|
505
|
+
>
|
|
506
|
+
{/* Your content */}
|
|
507
|
+
</AppLayout>
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
#### 2. **Create Translation Files**
|
|
513
|
+
|
|
514
|
+
Create JSON files for each language in your project:
|
|
515
|
+
|
|
516
|
+
```
|
|
517
|
+
/public
|
|
518
|
+
/translations
|
|
519
|
+
en.json
|
|
520
|
+
es.json
|
|
521
|
+
fr.json
|
|
522
|
+
pt.json
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
Example of a translation file (`en.json`):
|
|
526
|
+
|
|
527
|
+
```json
|
|
528
|
+
{
|
|
529
|
+
"common": {
|
|
530
|
+
"save": "Save",
|
|
531
|
+
"cancel": "Cancel",
|
|
532
|
+
"submit": "Submit"
|
|
533
|
+
},
|
|
534
|
+
"auth": {
|
|
535
|
+
"login": "Login",
|
|
536
|
+
"register": "Register",
|
|
537
|
+
"forgotPassword": "Forgot Password?"
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
#### 3. **Add i18next-scanner Config**
|
|
543
|
+
|
|
544
|
+
Create an `i18next-scanner.config.js` in your project root:
|
|
545
|
+
|
|
546
|
+
```js
|
|
547
|
+
module.exports = {
|
|
548
|
+
input: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.test.{js,jsx,ts,tsx}', '!**/node_modules/**'],
|
|
549
|
+
output: './public/translations/',
|
|
550
|
+
options: {
|
|
551
|
+
debug: true,
|
|
552
|
+
func: {
|
|
553
|
+
list: ['t', 'tLib'],
|
|
554
|
+
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
|
555
|
+
},
|
|
556
|
+
lngs: ['en', 'es', 'fr', 'pt'],
|
|
557
|
+
defaultLng: 'en',
|
|
558
|
+
defaultValue: function(lng, ns, key) {
|
|
559
|
+
return key;
|
|
560
|
+
},
|
|
561
|
+
resource: {
|
|
562
|
+
loadPath: 'public/translations/{{lng}}.json',
|
|
563
|
+
savePath: '{{lng}}.json',
|
|
564
|
+
jsonIndent: 2,
|
|
565
|
+
lineEnding: '\n',
|
|
566
|
+
},
|
|
567
|
+
removeUnusedKeys: false,
|
|
568
|
+
nsSeparator: false,
|
|
569
|
+
keySeparator: '.',
|
|
570
|
+
},
|
|
571
|
+
};
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
#### 4. **Add Extraction Script to package.json**
|
|
575
|
+
|
|
576
|
+
```json
|
|
577
|
+
"scripts": {
|
|
578
|
+
"extract-translations": "i18next-scanner --config i18next-scanner.config.js 'src/**/*.{js,jsx,ts,tsx}'"
|
|
579
|
+
}
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
#### 5. **Install Required Dependencies**
|
|
583
|
+
|
|
584
|
+
Install the necessary dev dependencies:
|
|
585
|
+
|
|
586
|
+
```bash
|
|
587
|
+
yarn add -D i18next-scanner
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
Or if you're using npm:
|
|
591
|
+
|
|
592
|
+
```bash
|
|
593
|
+
npm install --save-dev i18next-scanner
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
#### 6. **Use Translations in Components**
|
|
597
|
+
|
|
598
|
+
```tsx
|
|
599
|
+
import { useTranslation } from 'pagamio-frontend-commons-lib';
|
|
600
|
+
|
|
601
|
+
function YourComponent() {
|
|
602
|
+
const { t } = useTranslation();
|
|
603
|
+
|
|
604
|
+
return (
|
|
605
|
+
<div>
|
|
606
|
+
<h1>{t('common.title', 'Default Title')}</h1>
|
|
607
|
+
<p>{t('common.description', 'This is a default description')}</p>
|
|
608
|
+
<button>{t('common.save', 'Save')}</button>
|
|
609
|
+
</div>
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
#### 7. **Use Library Translations**
|
|
615
|
+
|
|
616
|
+
The library provides common translations for frequently used UI elements:
|
|
617
|
+
|
|
618
|
+
```tsx
|
|
619
|
+
import { useLibTranslations } from 'pagamio-frontend-commons-lib';
|
|
620
|
+
|
|
621
|
+
function YourComponent() {
|
|
622
|
+
const { tLib } = useLibTranslations();
|
|
623
|
+
|
|
624
|
+
return (
|
|
625
|
+
<div>
|
|
626
|
+
<button>{tLib('save')}</button>
|
|
627
|
+
<button>{tLib('cancel')}</button>
|
|
628
|
+
<button>{tLib('submit')}</button>
|
|
629
|
+
</div>
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
#### 8. **Add Locale Switcher**
|
|
635
|
+
|
|
636
|
+
Add a language switcher to allow users to change the application language:
|
|
637
|
+
|
|
638
|
+
```tsx
|
|
639
|
+
import { LocaleSwitcher } from 'pagamio-frontend-commons-lib';
|
|
640
|
+
|
|
641
|
+
function Header() {
|
|
642
|
+
return (
|
|
643
|
+
<header>
|
|
644
|
+
<nav>
|
|
645
|
+
{/* Your other navigation elements */}
|
|
646
|
+
<LocaleSwitcher />
|
|
647
|
+
</nav>
|
|
648
|
+
</header>
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
You can customize language display names:
|
|
654
|
+
|
|
655
|
+
```tsx
|
|
656
|
+
<LocaleSwitcher
|
|
657
|
+
localeNames={{
|
|
658
|
+
en: 'English',
|
|
659
|
+
es: 'Español',
|
|
660
|
+
fr: 'Français',
|
|
661
|
+
pt: 'Português'
|
|
662
|
+
}}
|
|
663
|
+
/>
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
#### 9. **Run the Extraction Script**
|
|
667
|
+
|
|
668
|
+
Extract all translation keys from your application:
|
|
669
|
+
|
|
670
|
+
```bash
|
|
671
|
+
yarn extract-translations
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
This will scan your code for translation keys and update your translation files.
|
|
675
|
+
|
|
676
|
+
### **Advanced Configuration**
|
|
677
|
+
|
|
678
|
+
#### **Custom Locale Detector**
|
|
679
|
+
|
|
680
|
+
You can configure the translation system to detect the browser's locale:
|
|
681
|
+
|
|
682
|
+
```tsx
|
|
683
|
+
import { TranslationProvider, detectBrowserLocale } from 'pagamio-frontend-commons-lib';
|
|
684
|
+
|
|
685
|
+
function App() {
|
|
686
|
+
const browserLocale = detectBrowserLocale(); // Returns 'en', 'es', etc.
|
|
687
|
+
|
|
688
|
+
return (
|
|
689
|
+
<TranslationProvider
|
|
690
|
+
defaultLocale={browserLocale}
|
|
691
|
+
loadPath="/translations"
|
|
692
|
+
>
|
|
693
|
+
<YourApplicationComponents />
|
|
694
|
+
</TranslationProvider>
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
#### **Direct Loading of Translation Data**
|
|
700
|
+
|
|
701
|
+
Instead of loading translations from files, you can provide them directly:
|
|
702
|
+
|
|
703
|
+
```tsx
|
|
704
|
+
import { TranslationProvider } from 'pagamio-frontend-commons-lib';
|
|
705
|
+
|
|
706
|
+
const translations = [
|
|
707
|
+
{
|
|
708
|
+
locale: 'en',
|
|
709
|
+
messages: {
|
|
710
|
+
common: {
|
|
711
|
+
save: 'Save',
|
|
712
|
+
cancel: 'Cancel'
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
locale: 'es',
|
|
718
|
+
messages: {
|
|
719
|
+
common: {
|
|
720
|
+
save: 'Guardar',
|
|
721
|
+
cancel: 'Cancelar'
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
];
|
|
726
|
+
|
|
727
|
+
function App() {
|
|
728
|
+
return (
|
|
729
|
+
<TranslationProvider
|
|
730
|
+
defaultLocale="en"
|
|
731
|
+
localeData={translations}
|
|
732
|
+
>
|
|
733
|
+
<YourApplicationComponents />
|
|
734
|
+
</TranslationProvider>
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pagamio/frontend-commons-lib",
|
|
3
|
+
"description": "Pagamio library for Frontend reusable components like the form engine and table container",
|
|
4
|
+
"version": "0.8.175",
|
|
5
|
+
"private": false,
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"module": "lib/index.js",
|
|
8
|
+
"types": "lib/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./lib/index.d.ts",
|
|
12
|
+
"default": "./lib/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./components/layout": {
|
|
15
|
+
"types": "./lib/components/layout/index.d.ts",
|
|
16
|
+
"default": "./lib/components/layout/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./components/ui": {
|
|
19
|
+
"types": "./lib/components/ui/index.d.ts",
|
|
20
|
+
"default": "./lib/components/ui/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./components/icons": {
|
|
23
|
+
"types": "./lib/components/icons/index.d.ts",
|
|
24
|
+
"default": "./lib/components/icons/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./auth": {
|
|
27
|
+
"types": "./lib/auth/index.d.ts",
|
|
28
|
+
"default": "./lib/auth/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./api": {
|
|
31
|
+
"types": "./lib/api/index.d.ts",
|
|
32
|
+
"default": "./lib/api/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./context": {
|
|
35
|
+
"types": "./lib/context/index.d.ts",
|
|
36
|
+
"default": "./lib/context/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./shared": {
|
|
39
|
+
"types": "./lib/shared/index.d.ts",
|
|
40
|
+
"default": "./lib/shared/index.js"
|
|
41
|
+
},
|
|
42
|
+
"./rbac": {
|
|
43
|
+
"types": "./lib/rbac/index.d.ts",
|
|
44
|
+
"default": "./lib/rbac/index.js"
|
|
45
|
+
},
|
|
46
|
+
"./styles.css": "./lib/styles.css"
|
|
47
|
+
},
|
|
48
|
+
"sideEffects": [
|
|
49
|
+
"**/*.css",
|
|
50
|
+
"lib/styles.css"
|
|
51
|
+
],
|
|
52
|
+
"keywords": [
|
|
53
|
+
"react",
|
|
54
|
+
"table",
|
|
55
|
+
"form-engine",
|
|
56
|
+
"components"
|
|
57
|
+
],
|
|
58
|
+
"author": "Pagamio Dev Team <dev@pagmio.com>",
|
|
59
|
+
"files": [
|
|
60
|
+
"lib",
|
|
61
|
+
"README.md"
|
|
62
|
+
],
|
|
63
|
+
"scripts": {
|
|
64
|
+
"start:dev": "vite",
|
|
65
|
+
"start": "concurrently \"npm run build:watch\" \"npm run start:dev\"",
|
|
66
|
+
"test": "jest",
|
|
67
|
+
"build:watch": "tsc --watch",
|
|
68
|
+
"build": "rm -rf lib && tsc && yarn build:css && yarn copy-locale-files",
|
|
69
|
+
"build:css": "postcss src/index.css -o lib/styles.css",
|
|
70
|
+
"lint": "eslint 'src/**/*.{ts,tsx}'",
|
|
71
|
+
"lint:fix": "eslint . --fix",
|
|
72
|
+
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss}\"",
|
|
73
|
+
"format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx,json,css,scss}\"",
|
|
74
|
+
"prepare": "husky",
|
|
75
|
+
"validate-structure": "node validate-structure.js",
|
|
76
|
+
"copy-locale-files": "copyfiles -u 1 src/translations/locales/**/*.json lib",
|
|
77
|
+
"extract-translations": "i18next-scanner --config i18next-scanner.config.js 'src/**/*.{js,jsx,ts,tsx}'"
|
|
78
|
+
},
|
|
79
|
+
"dependencies": {
|
|
80
|
+
"@emotion/react": "^11.13.5",
|
|
81
|
+
"@heroicons/react": "^2.1.5",
|
|
82
|
+
"@hookform/resolvers": "^3.9.0",
|
|
83
|
+
"@mantine/core": "6.0.21",
|
|
84
|
+
"@mantine/dates": "6.0.21",
|
|
85
|
+
"@mantine/hooks": "6.0.21",
|
|
86
|
+
"@radix-ui/react-avatar": "^1.1.0",
|
|
87
|
+
"@radix-ui/react-checkbox": "^1.1.1",
|
|
88
|
+
"@radix-ui/react-dialog": "^1.1.2",
|
|
89
|
+
"@radix-ui/react-icons": "^1.3.0",
|
|
90
|
+
"@radix-ui/react-label": "^2.1.0",
|
|
91
|
+
"@radix-ui/react-popover": "^1.1.1",
|
|
92
|
+
"@radix-ui/react-radio-group": "^1.2.1",
|
|
93
|
+
"@radix-ui/react-select": "^2.1.1",
|
|
94
|
+
"@radix-ui/react-separator": "^1.1.0",
|
|
95
|
+
"@radix-ui/react-switch": "^1.1.1",
|
|
96
|
+
"@tabler/icons-react": "^3.23.0",
|
|
97
|
+
"@tanstack/react-table": "^8.20.5",
|
|
98
|
+
"@testing-library/react": "^16.0.1",
|
|
99
|
+
"@types/file-saver": "^2.0.7",
|
|
100
|
+
"@types/papaparse": "^5.3.15",
|
|
101
|
+
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
|
102
|
+
"@typescript-eslint/parser": "^8.26.1",
|
|
103
|
+
"apexcharts": "^4.4.0",
|
|
104
|
+
"class-variance-authority": "^0.7.0",
|
|
105
|
+
"classnames": "^2.5.1",
|
|
106
|
+
"clsx": "^2.1.1",
|
|
107
|
+
"cmdk": "1.0.0",
|
|
108
|
+
"date-fns": "^4.1.0",
|
|
109
|
+
"dayjs": "^1.11.13",
|
|
110
|
+
"echarts": "^5.5.1",
|
|
111
|
+
"echarts-for-react": "^3.0.2",
|
|
112
|
+
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
113
|
+
"eslint-plugin-unicorn": "^57.0.0",
|
|
114
|
+
"file-saver": "^2.0.5",
|
|
115
|
+
"flowbite": "^2.5.2",
|
|
116
|
+
"flowbite-react": "^0.10.2",
|
|
117
|
+
"js-cookie": "^3.0.5",
|
|
118
|
+
"jspdf": "^2.5.2",
|
|
119
|
+
"jspdf-autotable": "^3.8.4",
|
|
120
|
+
"jwt-decode": "^4.0.0",
|
|
121
|
+
"lodash": "^4.17.21",
|
|
122
|
+
"lucide-react": "^0.441.0",
|
|
123
|
+
"mantine-react-table": "^1.3.4",
|
|
124
|
+
"papaparse": "^5.4.1",
|
|
125
|
+
"react": "^18",
|
|
126
|
+
"react-apexcharts": "^1.7.0",
|
|
127
|
+
"react-date-range": "^2.0.1",
|
|
128
|
+
"react-datepicker": "^7.6.0",
|
|
129
|
+
"react-day-picker": "^9.4.4",
|
|
130
|
+
"react-dom": "^18",
|
|
131
|
+
"react-dropzone": "^14.2.3",
|
|
132
|
+
"react-hook-form": "^7.53.1",
|
|
133
|
+
"react-icons": "^5.3.0",
|
|
134
|
+
"react-phone-number-input": "^3.4.12",
|
|
135
|
+
"react-table": "^7.8.0",
|
|
136
|
+
"swr": "^2.3.3",
|
|
137
|
+
"tailwind-merge": "^2.5.2",
|
|
138
|
+
"tailwindcss-animate": "^1.0.7",
|
|
139
|
+
"uuid": "^11.1.0",
|
|
140
|
+
"xlsx": "^0.18.5",
|
|
141
|
+
"zod": "^3.23.8"
|
|
142
|
+
},
|
|
143
|
+
"devDependencies": {
|
|
144
|
+
"@next/eslint-plugin-next": "^15.2.2",
|
|
145
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
146
|
+
"@testing-library/user-event": "^14.5.2",
|
|
147
|
+
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
|
148
|
+
"@types/jest": "^29.5.14",
|
|
149
|
+
"@types/js-cookie": "^3.0.6",
|
|
150
|
+
"@types/lodash": "^4.17.13",
|
|
151
|
+
"@types/node": "^20",
|
|
152
|
+
"@types/react": "^18",
|
|
153
|
+
"@types/react-date-range": "^1.4.10",
|
|
154
|
+
"@types/react-dom": "^18",
|
|
155
|
+
"@types/react-dropzone": "^5.1.0",
|
|
156
|
+
"@types/react-table": "^7.7.20",
|
|
157
|
+
"@vitejs/plugin-react": "^4.3.3",
|
|
158
|
+
"autoprefixer": "^10.4.20",
|
|
159
|
+
"concurrently": "^9.0.1",
|
|
160
|
+
"copyfiles": "^2.4.1",
|
|
161
|
+
"eslint": "^9.22.0",
|
|
162
|
+
"eslint-config-prettier": "^10.0.1",
|
|
163
|
+
"eslint-plugin-prettier": "^5.2.3",
|
|
164
|
+
"eslint-plugin-react": "^7.37.4",
|
|
165
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
166
|
+
"eslint-plugin-tailwindcss": "^3.18.0",
|
|
167
|
+
"husky": "^9.1.7",
|
|
168
|
+
"i18next-scanner": "^4.4.0",
|
|
169
|
+
"lint-staged": "^15.5.0",
|
|
170
|
+
"postcss": "^8.4.47",
|
|
171
|
+
"postcss-cli": "^11.0.0",
|
|
172
|
+
"prettier": "^3.4.2",
|
|
173
|
+
"tailwindcss": "^3.4.1",
|
|
174
|
+
"typescript": "^5",
|
|
175
|
+
"vite": "^4.0.0"
|
|
176
|
+
},
|
|
177
|
+
"lint-staged": {
|
|
178
|
+
"*.{js,ts,tsx}": "eslint . --fix"
|
|
179
|
+
}
|
|
180
|
+
}
|