em-crm-claim-module
v1.0.0
Published
A responsive React component module for managing claims - Add Claim and List Claims
Maintainers
Readme
em-crm-claim-module
A responsive React component module for managing claims - Add Claim and List Claims functionality extracted from em-crm project. The module handles all API calls internally, requiring only baseURL, token, and userData as inputs.
Features
- ✅ Add Claim Component - Responsive form to create new claims
- ✅ Claims List Component - Responsive table to view and manage claims
- ✅ Internal API Handling - All API calls are handled within the package
- ✅ Fully Responsive - Works seamlessly on mobile, tablet, and desktop
- ✅ Material-UI Integration - Built with Material-UI v5
- ✅ Auto-refresh - Claims list auto-refreshes at configurable intervals
- ✅ TypeScript Ready - Can be used with TypeScript projects
Installation
npm install em-crm-claim-modulePeer Dependencies
Make sure you have these peer dependencies installed:
npm install react react-dom @mui/material @mui/icons-material @mui/x-date-pickers react-select@^5.0.0 moment react-hot-toast axios@^1.0.0 dayjsNotes:
react-selectversion 5.x is required for React 18 compatibility. Version 4.x only supports React 16-17.axiosversion 1.x is required for security. Versions < 1.0.0 have known vulnerabilities (CSRF, DoS, SSRF).
Quick Start
The module requires three essential inputs:
baseURL- Your API base URLtoken- Authentication tokenuserData- User information object
Optional (recommended):
refreshToken- Refresh token for automatic token renewalonTokenRefresh- Callback function to handle token refresh
import React from 'react';
import { ClaimProvider, AddClaim, ClaimsList } from 'em-crm-claim-module';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import 'em-crm-claim-module/dist/index.css';
const theme = createTheme({
palette: {
primary: {
main: '#f45e29',
},
},
});
function App() {
const baseURL = 'https://api.example.com'; // Your API base URL
const token = 'your-auth-token'; // Your authentication token
const refreshToken = 'your-refresh-token'; // Optional: for automatic token refresh
// Token refresh handler (optional but recommended)
const handleTokenRefresh = async (refreshTokenValue) => {
const response = await fetch(`${baseURL}/api/auth/refresh`, {
method: 'POST',
body: JSON.stringify({ refreshToken: refreshTokenValue }),
});
const data = await response.json();
return data.token || data.accessToken;
};
const userData = {
crm_role: 'EMPLOYEE',
name: 'John Doe',
crm_profile: 'SALES',
employee_code: 'EMP001',
username: 'johndoe',
uuid: 'user-uuid-123',
};
return (
<ThemeProvider theme={theme}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<ClaimProvider
baseURL={baseURL}
token={token}
refreshToken={refreshToken}
onTokenRefresh={handleTokenRefresh}
userData={userData}
>
{/* Your components */}
<ClaimsList />
</ClaimProvider>
</LocalizationProvider>
</ThemeProvider>
);
}Usage
Basic Setup
Wrap your application with ClaimProvider and provide the required props:
import { ClaimProvider, AddClaim, ClaimsList } from 'em-crm-claim-module';
import axios from 'axios';
function App() {
const baseURL = 'https://api.example.com';
const token = localStorage.getItem('authToken');
const refreshToken = localStorage.getItem('refreshToken');
// Token refresh handler
const handleTokenRefresh = async (refreshTokenValue) => {
try {
const response = await axios.post(`${baseURL}/api/auth/refresh`, {
refreshToken: refreshTokenValue,
});
const newToken = response.data?.token || response.data?.accessToken;
if (newToken) {
localStorage.setItem('authToken', newToken);
return newToken;
}
} catch (error) {
// Handle refresh failure (e.g., redirect to login)
console.error('Token refresh failed:', error);
throw error;
}
};
return (
<ClaimProvider
baseURL={baseURL}
token={token}
refreshToken={refreshToken}
onTokenRefresh={handleTokenRefresh}
userData={{
crm_role: 'EMPLOYEE',
name: 'John Doe',
crm_profile: 'SALES',
employee_code: 'EMP001',
username: 'johndoe',
uuid: 'user-uuid-123',
}}
>
<ClaimsList />
</ClaimProvider>
);
}AddClaim Component
import { ClaimProvider, AddClaim } from 'em-crm-claim-module';
function AddClaimPage() {
const [view, setView] = useState('list');
return (
<ClaimProvider baseURL={baseURL} token={token} userData={userData}>
<AddClaim
onSuccess={(data) => {
console.log('Claim created:', data);
setView('list'); // Navigate back to list
}}
onCancel={() => setView('list')}
/>
</ClaimProvider>
);
}ClaimsList Component
import { ClaimProvider, ClaimsList } from 'em-crm-claim-module';
function ClaimsListPage() {
const [view, setView] = useState('list');
return (
<ClaimProvider baseURL={baseURL} token={token} userData={userData}>
<ClaimsList
onAddClaim={() => setView('add')}
onRefresh={() => {
console.log('Claims refreshed');
}}
autoRefresh={true}
refreshInterval={30000} // Refresh every 30 seconds
/>
</ClaimProvider>
);
}Complete Example with Navigation and Refresh Token
import React, { useState } from 'react';
import { ClaimProvider, AddClaim, ClaimsList } from 'em-crm-claim-module';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import axios from 'axios';
function ClaimModule() {
const [view, setView] = useState('list'); // 'list' or 'add'
const baseURL = process.env.REACT_APP_API_URL || 'https://api.example.com';
const token = localStorage.getItem('authToken');
const refreshToken = localStorage.getItem('refreshToken');
// Token refresh handler
const handleTokenRefresh = async (refreshTokenValue) => {
try {
const response = await axios.post(`${baseURL}/api/auth/refresh`, {
refreshToken: refreshTokenValue,
});
const newToken = response.data?.token || response.data?.accessToken;
if (newToken) {
localStorage.setItem('authToken', newToken);
// Update refresh token if provided
if (response.data?.refreshToken) {
localStorage.setItem('refreshToken', response.data.refreshToken);
}
return newToken;
}
throw new Error('No token received');
} catch (error) {
console.error('Token refresh failed:', error);
// Redirect to login on refresh failure
// window.location.href = '/login';
throw error;
}
};
const userData = {
crm_role: 'EMPLOYEE',
name: 'John Doe',
crm_profile: 'SALES',
employee_code: 'EMP001',
username: 'johndoe',
uuid: 'user-uuid-123',
};
return (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<ClaimProvider
baseURL={baseURL}
token={token}
refreshToken={refreshToken}
onTokenRefresh={handleTokenRefresh}
userData={userData}
>
{view === 'add' ? (
<AddClaim
onSuccess={() => setView('list')}
onCancel={() => setView('list')}
/>
) : (
<ClaimsList
onAddClaim={() => setView('add')}
/>
)}
</ClaimProvider>
</LocalizationProvider>
);
}Refresh Token Handling
The module supports automatic token refresh when API calls receive a 401 (Unauthorized) response. There are two ways to handle token refresh:
Option 1: Custom Refresh Handler (Recommended)
Provide a callback function that handles the token refresh:
const handleTokenRefresh = async (refreshTokenValue) => {
const response = await fetch(`${baseURL}/api/auth/refresh`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken: refreshTokenValue }),
});
const data = await response.json();
return data.token || data.accessToken;
};
<ClaimProvider
baseURL={baseURL}
token={token}
refreshToken={refreshToken}
onTokenRefresh={handleTokenRefresh}
userData={userData}
>
{/* ... */}
</ClaimProvider>Option 2: Automatic Refresh
Set onTokenRefresh to 'auto' to use the standard endpoint /api/auth/refresh:
<ClaimProvider
baseURL={baseURL}
token={token}
refreshToken={refreshToken}
onTokenRefresh="auto"
userData={userData}
>
{/* ... */}
</ClaimProvider>How it works:
- When any API call receives a 401 response, the module automatically attempts to refresh the token
- All pending requests are queued and retried after a successful token refresh
- If token refresh fails, all queued requests are rejected and an error is shown
- The new token is automatically updated in the API service
Props
ClaimProvider Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| baseURL | string | Yes | Base URL for API calls (e.g., 'https://api.example.com') |
| token | string | Yes | Authentication token for API requests |
| userData | object | Yes | User data object (see UserData structure below) |
| refreshToken | string | No | Refresh token for automatic token renewal |
| onTokenRefresh | function \| string | No | Callback function to refresh token, or 'auto' for automatic refresh using /api/auth/refresh endpoint |
| children | ReactNode | Yes | React children components |
AddClaim Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| onSuccess | function | No | Callback function called when claim is successfully created |
| onCancel | function | No | Callback function called when cancel button is clicked |
| visitOptions | array | No | Custom visit purpose options |
ClaimsList Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| onAddClaim | function | No | Callback function called when Add Claim button is clicked |
| onRefresh | function | No | Callback function called after claims are refreshed |
| columns | array | No | Custom column configuration |
| autoRefresh | boolean | No | Enable auto-refresh (default: true) |
| refreshInterval | number | No | Auto-refresh interval in milliseconds (default: 30000) |
Data Structures
UserData Object
{
crm_role: 'EMPLOYEE', // User's role
name: 'John Doe', // User's name
crm_profile: 'SALES', // User's profile
employee_code: 'EMP001', // Employee code
username: 'johndoe', // Username
uuid: 'user-uuid-123', // User UUID
// Optional fields:
roleId: 'EMPLOYEE', // Alternative role ID
profileName: 'SALES', // Alternative profile name
empCode: 'EMP001', // Alternative employee code
}Claim Object Structure
The API should return claim objects with the following structure:
{
_id: 'claim-id',
claimId: 'CLM001',
requestBy_name: 'John Doe',
schoolName: 'ABC School',
expenseType: 'Food',
claimStatus: 'PENDING AT BUH',
createdAt: '2024-01-01T00:00:00Z',
approvedDate: '2024-01-02T00:00:00Z',
claimAmount: 1000,
approvedAmount: 950
}API Endpoints
The module expects the following API endpoints:
POST /api/userClaim/createMyClaim- Create a new claimPOST /api/userClaim/getMyClaimList- Get list of claimsPUT /api/userClaim/bulkDelete- Delete multiple claimsGET /api/claimMaster/getClaimMasterList- Get claim master listGET /api/school/getSchoolCodeList- Search schools
All endpoints should:
- Accept Bearer token authentication in the
Authorizationheader - Return data in the format:
{ result: [...], message: '...' }
Responsive Design
Both components are fully responsive and adapt to different screen sizes:
- Mobile (< 768px): Stacked layout, optimized for touch interactions
- Tablet (768px - 1024px): Balanced layout with some columns hidden
- Desktop (> 1024px): Full layout with all columns visible
Styling
The components use Material-UI's theming system. You can customize the appearance by wrapping your app with a Material-UI ThemeProvider:
import { ThemeProvider, createTheme } from '@mui/material/styles';
const theme = createTheme({
palette: {
primary: {
main: '#f45e29',
},
},
});
function App() {
return (
<ThemeProvider theme={theme}>
{/* Your components */}
</ThemeProvider>
);
}Error Handling
The module handles errors internally and displays toast notifications using react-hot-toast. Common errors:
- 401 Unauthorized: Token is invalid or expired - automatically triggers token refresh if
refreshTokenandonTokenRefreshare provided - Network errors: Connection issues are displayed to the user
- Validation errors: Form validation errors are shown inline
- Token refresh failures: If token refresh fails, a toast notification is shown and you can handle redirect to login in your
onTokenRefreshcallback
License
ISC
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
