em-crm-claim-module 1.0.0
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 +458 -0
- package/dist/index.css +1 -0
- package/dist/index.esm.css +1 -0
- package/dist/index.esm.js +22750 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +22774 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
# em-crm-claim-module
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Add Claim Component** - Responsive form to create new claims
|
|
8
|
+
- ✅ **Claims List Component** - Responsive table to view and manage claims
|
|
9
|
+
- ✅ **Internal API Handling** - All API calls are handled within the package
|
|
10
|
+
- ✅ **Fully Responsive** - Works seamlessly on mobile, tablet, and desktop
|
|
11
|
+
- ✅ **Material-UI Integration** - Built with Material-UI v5
|
|
12
|
+
- ✅ **Auto-refresh** - Claims list auto-refreshes at configurable intervals
|
|
13
|
+
- ✅ **TypeScript Ready** - Can be used with TypeScript projects
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install em-crm-claim-module
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Peer Dependencies
|
|
22
|
+
|
|
23
|
+
Make sure you have these peer dependencies installed:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
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 dayjs
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Notes:**
|
|
30
|
+
- `react-select` version 5.x is required for React 18 compatibility. Version 4.x only supports React 16-17.
|
|
31
|
+
- `axios` version 1.x is required for security. Versions < 1.0.0 have known vulnerabilities (CSRF, DoS, SSRF).
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
The module requires three essential inputs:
|
|
36
|
+
- `baseURL` - Your API base URL
|
|
37
|
+
- `token` - Authentication token
|
|
38
|
+
- `userData` - User information object
|
|
39
|
+
|
|
40
|
+
**Optional (recommended):**
|
|
41
|
+
- `refreshToken` - Refresh token for automatic token renewal
|
|
42
|
+
- `onTokenRefresh` - Callback function to handle token refresh
|
|
43
|
+
|
|
44
|
+
```jsx
|
|
45
|
+
import React from 'react';
|
|
46
|
+
import { ClaimProvider, AddClaim, ClaimsList } from 'em-crm-claim-module';
|
|
47
|
+
import { LocalizationProvider } from '@mui/x-date-pickers';
|
|
48
|
+
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
|
49
|
+
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
|
50
|
+
import 'em-crm-claim-module/dist/index.css';
|
|
51
|
+
|
|
52
|
+
const theme = createTheme({
|
|
53
|
+
palette: {
|
|
54
|
+
primary: {
|
|
55
|
+
main: '#f45e29',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function App() {
|
|
61
|
+
const baseURL = 'https://api.example.com'; // Your API base URL
|
|
62
|
+
const token = 'your-auth-token'; // Your authentication token
|
|
63
|
+
const refreshToken = 'your-refresh-token'; // Optional: for automatic token refresh
|
|
64
|
+
|
|
65
|
+
// Token refresh handler (optional but recommended)
|
|
66
|
+
const handleTokenRefresh = async (refreshTokenValue) => {
|
|
67
|
+
const response = await fetch(`${baseURL}/api/auth/refresh`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
body: JSON.stringify({ refreshToken: refreshTokenValue }),
|
|
70
|
+
});
|
|
71
|
+
const data = await response.json();
|
|
72
|
+
return data.token || data.accessToken;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const userData = {
|
|
76
|
+
crm_role: 'EMPLOYEE',
|
|
77
|
+
name: 'John Doe',
|
|
78
|
+
crm_profile: 'SALES',
|
|
79
|
+
employee_code: 'EMP001',
|
|
80
|
+
username: 'johndoe',
|
|
81
|
+
uuid: 'user-uuid-123',
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<ThemeProvider theme={theme}>
|
|
86
|
+
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
|
87
|
+
<ClaimProvider
|
|
88
|
+
baseURL={baseURL}
|
|
89
|
+
token={token}
|
|
90
|
+
refreshToken={refreshToken}
|
|
91
|
+
onTokenRefresh={handleTokenRefresh}
|
|
92
|
+
userData={userData}
|
|
93
|
+
>
|
|
94
|
+
{/* Your components */}
|
|
95
|
+
<ClaimsList />
|
|
96
|
+
</ClaimProvider>
|
|
97
|
+
</LocalizationProvider>
|
|
98
|
+
</ThemeProvider>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Usage
|
|
104
|
+
|
|
105
|
+
### Basic Setup
|
|
106
|
+
|
|
107
|
+
Wrap your application with `ClaimProvider` and provide the required props:
|
|
108
|
+
|
|
109
|
+
```jsx
|
|
110
|
+
import { ClaimProvider, AddClaim, ClaimsList } from 'em-crm-claim-module';
|
|
111
|
+
import axios from 'axios';
|
|
112
|
+
|
|
113
|
+
function App() {
|
|
114
|
+
const baseURL = 'https://api.example.com';
|
|
115
|
+
const token = localStorage.getItem('authToken');
|
|
116
|
+
const refreshToken = localStorage.getItem('refreshToken');
|
|
117
|
+
|
|
118
|
+
// Token refresh handler
|
|
119
|
+
const handleTokenRefresh = async (refreshTokenValue) => {
|
|
120
|
+
try {
|
|
121
|
+
const response = await axios.post(`${baseURL}/api/auth/refresh`, {
|
|
122
|
+
refreshToken: refreshTokenValue,
|
|
123
|
+
});
|
|
124
|
+
const newToken = response.data?.token || response.data?.accessToken;
|
|
125
|
+
if (newToken) {
|
|
126
|
+
localStorage.setItem('authToken', newToken);
|
|
127
|
+
return newToken;
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
// Handle refresh failure (e.g., redirect to login)
|
|
131
|
+
console.error('Token refresh failed:', error);
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<ClaimProvider
|
|
138
|
+
baseURL={baseURL}
|
|
139
|
+
token={token}
|
|
140
|
+
refreshToken={refreshToken}
|
|
141
|
+
onTokenRefresh={handleTokenRefresh}
|
|
142
|
+
userData={{
|
|
143
|
+
crm_role: 'EMPLOYEE',
|
|
144
|
+
name: 'John Doe',
|
|
145
|
+
crm_profile: 'SALES',
|
|
146
|
+
employee_code: 'EMP001',
|
|
147
|
+
username: 'johndoe',
|
|
148
|
+
uuid: 'user-uuid-123',
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
<ClaimsList />
|
|
152
|
+
</ClaimProvider>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### AddClaim Component
|
|
158
|
+
|
|
159
|
+
```jsx
|
|
160
|
+
import { ClaimProvider, AddClaim } from 'em-crm-claim-module';
|
|
161
|
+
|
|
162
|
+
function AddClaimPage() {
|
|
163
|
+
const [view, setView] = useState('list');
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<ClaimProvider baseURL={baseURL} token={token} userData={userData}>
|
|
167
|
+
<AddClaim
|
|
168
|
+
onSuccess={(data) => {
|
|
169
|
+
console.log('Claim created:', data);
|
|
170
|
+
setView('list'); // Navigate back to list
|
|
171
|
+
}}
|
|
172
|
+
onCancel={() => setView('list')}
|
|
173
|
+
/>
|
|
174
|
+
</ClaimProvider>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### ClaimsList Component
|
|
180
|
+
|
|
181
|
+
```jsx
|
|
182
|
+
import { ClaimProvider, ClaimsList } from 'em-crm-claim-module';
|
|
183
|
+
|
|
184
|
+
function ClaimsListPage() {
|
|
185
|
+
const [view, setView] = useState('list');
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<ClaimProvider baseURL={baseURL} token={token} userData={userData}>
|
|
189
|
+
<ClaimsList
|
|
190
|
+
onAddClaim={() => setView('add')}
|
|
191
|
+
onRefresh={() => {
|
|
192
|
+
console.log('Claims refreshed');
|
|
193
|
+
}}
|
|
194
|
+
autoRefresh={true}
|
|
195
|
+
refreshInterval={30000} // Refresh every 30 seconds
|
|
196
|
+
/>
|
|
197
|
+
</ClaimProvider>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Complete Example with Navigation and Refresh Token
|
|
203
|
+
|
|
204
|
+
```jsx
|
|
205
|
+
import React, { useState } from 'react';
|
|
206
|
+
import { ClaimProvider, AddClaim, ClaimsList } from 'em-crm-claim-module';
|
|
207
|
+
import { LocalizationProvider } from '@mui/x-date-pickers';
|
|
208
|
+
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
|
209
|
+
import axios from 'axios';
|
|
210
|
+
|
|
211
|
+
function ClaimModule() {
|
|
212
|
+
const [view, setView] = useState('list'); // 'list' or 'add'
|
|
213
|
+
|
|
214
|
+
const baseURL = process.env.REACT_APP_API_URL || 'https://api.example.com';
|
|
215
|
+
const token = localStorage.getItem('authToken');
|
|
216
|
+
const refreshToken = localStorage.getItem('refreshToken');
|
|
217
|
+
|
|
218
|
+
// Token refresh handler
|
|
219
|
+
const handleTokenRefresh = async (refreshTokenValue) => {
|
|
220
|
+
try {
|
|
221
|
+
const response = await axios.post(`${baseURL}/api/auth/refresh`, {
|
|
222
|
+
refreshToken: refreshTokenValue,
|
|
223
|
+
});
|
|
224
|
+
const newToken = response.data?.token || response.data?.accessToken;
|
|
225
|
+
if (newToken) {
|
|
226
|
+
localStorage.setItem('authToken', newToken);
|
|
227
|
+
// Update refresh token if provided
|
|
228
|
+
if (response.data?.refreshToken) {
|
|
229
|
+
localStorage.setItem('refreshToken', response.data.refreshToken);
|
|
230
|
+
}
|
|
231
|
+
return newToken;
|
|
232
|
+
}
|
|
233
|
+
throw new Error('No token received');
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.error('Token refresh failed:', error);
|
|
236
|
+
// Redirect to login on refresh failure
|
|
237
|
+
// window.location.href = '/login';
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const userData = {
|
|
243
|
+
crm_role: 'EMPLOYEE',
|
|
244
|
+
name: 'John Doe',
|
|
245
|
+
crm_profile: 'SALES',
|
|
246
|
+
employee_code: 'EMP001',
|
|
247
|
+
username: 'johndoe',
|
|
248
|
+
uuid: 'user-uuid-123',
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
return (
|
|
252
|
+
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
|
253
|
+
<ClaimProvider
|
|
254
|
+
baseURL={baseURL}
|
|
255
|
+
token={token}
|
|
256
|
+
refreshToken={refreshToken}
|
|
257
|
+
onTokenRefresh={handleTokenRefresh}
|
|
258
|
+
userData={userData}
|
|
259
|
+
>
|
|
260
|
+
{view === 'add' ? (
|
|
261
|
+
<AddClaim
|
|
262
|
+
onSuccess={() => setView('list')}
|
|
263
|
+
onCancel={() => setView('list')}
|
|
264
|
+
/>
|
|
265
|
+
) : (
|
|
266
|
+
<ClaimsList
|
|
267
|
+
onAddClaim={() => setView('add')}
|
|
268
|
+
/>
|
|
269
|
+
)}
|
|
270
|
+
</ClaimProvider>
|
|
271
|
+
</LocalizationProvider>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Refresh Token Handling
|
|
277
|
+
|
|
278
|
+
The module supports automatic token refresh when API calls receive a 401 (Unauthorized) response. There are two ways to handle token refresh:
|
|
279
|
+
|
|
280
|
+
#### Option 1: Custom Refresh Handler (Recommended)
|
|
281
|
+
|
|
282
|
+
Provide a callback function that handles the token refresh:
|
|
283
|
+
|
|
284
|
+
```jsx
|
|
285
|
+
const handleTokenRefresh = async (refreshTokenValue) => {
|
|
286
|
+
const response = await fetch(`${baseURL}/api/auth/refresh`, {
|
|
287
|
+
method: 'POST',
|
|
288
|
+
headers: { 'Content-Type': 'application/json' },
|
|
289
|
+
body: JSON.stringify({ refreshToken: refreshTokenValue }),
|
|
290
|
+
});
|
|
291
|
+
const data = await response.json();
|
|
292
|
+
return data.token || data.accessToken;
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
<ClaimProvider
|
|
296
|
+
baseURL={baseURL}
|
|
297
|
+
token={token}
|
|
298
|
+
refreshToken={refreshToken}
|
|
299
|
+
onTokenRefresh={handleTokenRefresh}
|
|
300
|
+
userData={userData}
|
|
301
|
+
>
|
|
302
|
+
{/* ... */}
|
|
303
|
+
</ClaimProvider>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
#### Option 2: Automatic Refresh
|
|
307
|
+
|
|
308
|
+
Set `onTokenRefresh` to `'auto'` to use the standard endpoint `/api/auth/refresh`:
|
|
309
|
+
|
|
310
|
+
```jsx
|
|
311
|
+
<ClaimProvider
|
|
312
|
+
baseURL={baseURL}
|
|
313
|
+
token={token}
|
|
314
|
+
refreshToken={refreshToken}
|
|
315
|
+
onTokenRefresh="auto"
|
|
316
|
+
userData={userData}
|
|
317
|
+
>
|
|
318
|
+
{/* ... */}
|
|
319
|
+
</ClaimProvider>
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**How it works:**
|
|
323
|
+
- When any API call receives a 401 response, the module automatically attempts to refresh the token
|
|
324
|
+
- All pending requests are queued and retried after a successful token refresh
|
|
325
|
+
- If token refresh fails, all queued requests are rejected and an error is shown
|
|
326
|
+
- The new token is automatically updated in the API service
|
|
327
|
+
|
|
328
|
+
## Props
|
|
329
|
+
|
|
330
|
+
### ClaimProvider Props
|
|
331
|
+
|
|
332
|
+
| Prop | Type | Required | Description |
|
|
333
|
+
|------|------|----------|-------------|
|
|
334
|
+
| `baseURL` | `string` | Yes | Base URL for API calls (e.g., 'https://api.example.com') |
|
|
335
|
+
| `token` | `string` | Yes | Authentication token for API requests |
|
|
336
|
+
| `userData` | `object` | Yes | User data object (see UserData structure below) |
|
|
337
|
+
| `refreshToken` | `string` | No | Refresh token for automatic token renewal |
|
|
338
|
+
| `onTokenRefresh` | `function \| string` | No | Callback function to refresh token, or 'auto' for automatic refresh using `/api/auth/refresh` endpoint |
|
|
339
|
+
| `children` | `ReactNode` | Yes | React children components |
|
|
340
|
+
|
|
341
|
+
### AddClaim Props
|
|
342
|
+
|
|
343
|
+
| Prop | Type | Required | Description |
|
|
344
|
+
|------|------|----------|-------------|
|
|
345
|
+
| `onSuccess` | `function` | No | Callback function called when claim is successfully created |
|
|
346
|
+
| `onCancel` | `function` | No | Callback function called when cancel button is clicked |
|
|
347
|
+
| `visitOptions` | `array` | No | Custom visit purpose options |
|
|
348
|
+
|
|
349
|
+
### ClaimsList Props
|
|
350
|
+
|
|
351
|
+
| Prop | Type | Required | Description |
|
|
352
|
+
|------|------|----------|-------------|
|
|
353
|
+
| `onAddClaim` | `function` | No | Callback function called when Add Claim button is clicked |
|
|
354
|
+
| `onRefresh` | `function` | No | Callback function called after claims are refreshed |
|
|
355
|
+
| `columns` | `array` | No | Custom column configuration |
|
|
356
|
+
| `autoRefresh` | `boolean` | No | Enable auto-refresh (default: true) |
|
|
357
|
+
| `refreshInterval` | `number` | No | Auto-refresh interval in milliseconds (default: 30000) |
|
|
358
|
+
|
|
359
|
+
## Data Structures
|
|
360
|
+
|
|
361
|
+
### UserData Object
|
|
362
|
+
|
|
363
|
+
```javascript
|
|
364
|
+
{
|
|
365
|
+
crm_role: 'EMPLOYEE', // User's role
|
|
366
|
+
name: 'John Doe', // User's name
|
|
367
|
+
crm_profile: 'SALES', // User's profile
|
|
368
|
+
employee_code: 'EMP001', // Employee code
|
|
369
|
+
username: 'johndoe', // Username
|
|
370
|
+
uuid: 'user-uuid-123', // User UUID
|
|
371
|
+
// Optional fields:
|
|
372
|
+
roleId: 'EMPLOYEE', // Alternative role ID
|
|
373
|
+
profileName: 'SALES', // Alternative profile name
|
|
374
|
+
empCode: 'EMP001', // Alternative employee code
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Claim Object Structure
|
|
379
|
+
|
|
380
|
+
The API should return claim objects with the following structure:
|
|
381
|
+
|
|
382
|
+
```javascript
|
|
383
|
+
{
|
|
384
|
+
_id: 'claim-id',
|
|
385
|
+
claimId: 'CLM001',
|
|
386
|
+
requestBy_name: 'John Doe',
|
|
387
|
+
schoolName: 'ABC School',
|
|
388
|
+
expenseType: 'Food',
|
|
389
|
+
claimStatus: 'PENDING AT BUH',
|
|
390
|
+
createdAt: '2024-01-01T00:00:00Z',
|
|
391
|
+
approvedDate: '2024-01-02T00:00:00Z',
|
|
392
|
+
claimAmount: 1000,
|
|
393
|
+
approvedAmount: 950
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## API Endpoints
|
|
398
|
+
|
|
399
|
+
The module expects the following API endpoints:
|
|
400
|
+
|
|
401
|
+
- `POST /api/userClaim/createMyClaim` - Create a new claim
|
|
402
|
+
- `POST /api/userClaim/getMyClaimList` - Get list of claims
|
|
403
|
+
- `PUT /api/userClaim/bulkDelete` - Delete multiple claims
|
|
404
|
+
- `GET /api/claimMaster/getClaimMasterList` - Get claim master list
|
|
405
|
+
- `GET /api/school/getSchoolCodeList` - Search schools
|
|
406
|
+
|
|
407
|
+
All endpoints should:
|
|
408
|
+
- Accept Bearer token authentication in the `Authorization` header
|
|
409
|
+
- Return data in the format: `{ result: [...], message: '...' }`
|
|
410
|
+
|
|
411
|
+
## Responsive Design
|
|
412
|
+
|
|
413
|
+
Both components are fully responsive and adapt to different screen sizes:
|
|
414
|
+
|
|
415
|
+
- **Mobile (< 768px)**: Stacked layout, optimized for touch interactions
|
|
416
|
+
- **Tablet (768px - 1024px)**: Balanced layout with some columns hidden
|
|
417
|
+
- **Desktop (> 1024px)**: Full layout with all columns visible
|
|
418
|
+
|
|
419
|
+
## Styling
|
|
420
|
+
|
|
421
|
+
The components use Material-UI's theming system. You can customize the appearance by wrapping your app with a Material-UI ThemeProvider:
|
|
422
|
+
|
|
423
|
+
```jsx
|
|
424
|
+
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
|
425
|
+
|
|
426
|
+
const theme = createTheme({
|
|
427
|
+
palette: {
|
|
428
|
+
primary: {
|
|
429
|
+
main: '#f45e29',
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
function App() {
|
|
435
|
+
return (
|
|
436
|
+
<ThemeProvider theme={theme}>
|
|
437
|
+
{/* Your components */}
|
|
438
|
+
</ThemeProvider>
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## Error Handling
|
|
444
|
+
|
|
445
|
+
The module handles errors internally and displays toast notifications using `react-hot-toast`. Common errors:
|
|
446
|
+
|
|
447
|
+
- **401 Unauthorized**: Token is invalid or expired - automatically triggers token refresh if `refreshToken` and `onTokenRefresh` are provided
|
|
448
|
+
- **Network errors**: Connection issues are displayed to the user
|
|
449
|
+
- **Validation errors**: Form validation errors are shown inline
|
|
450
|
+
- **Token refresh failures**: If token refresh fails, a toast notification is shown and you can handle redirect to login in your `onTokenRefresh` callback
|
|
451
|
+
|
|
452
|
+
## License
|
|
453
|
+
|
|
454
|
+
ISC
|
|
455
|
+
|
|
456
|
+
## Contributing
|
|
457
|
+
|
|
458
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
package/dist/index.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.rc-time-picker{display:inline-block;position:relative}.rc-time-picker,.rc-time-picker *{box-sizing:border-box}.rc-time-picker-clear{cursor:pointer;height:20px;line-height:20px;margin:0;overflow:hidden;position:absolute;right:6px;text-align:center;top:3px;width:20px}.rc-time-picker-clear-icon:after{color:#aaa;content:"x";display:inline-block;font-size:12px;font-style:normal;height:20px;line-height:1;-webkit-transition:color .3s ease;transition:color .3s ease;width:20px}.rc-time-picker-clear-icon:hover:after{color:#666}.rc-time-picker-input{background-color:#fff;background-image:none;border:1px solid #d9d9d9;border-radius:4px;color:#666;cursor:text;display:inline-block;font-size:12px;height:28px;line-height:1.5;padding:4px 7px;position:relative;-webkit-transition:border .2s cubic-bezier(.645,.045,.355,1),background .2s cubic-bezier(.645,.045,.355,1),box-shadow .2s cubic-bezier(.645,.045,.355,1);transition:border .2s cubic-bezier(.645,.045,.355,1),background .2s cubic-bezier(.645,.045,.355,1),box-shadow .2s cubic-bezier(.645,.045,.355,1);width:100%}.rc-time-picker-input[disabled]{background:#f7f7f7;color:#ccc;cursor:not-allowed}.rc-time-picker-panel{box-sizing:border-box;position:absolute;width:170px;z-index:1070}.rc-time-picker-panel *{box-sizing:border-box}.rc-time-picker-panel-inner{background-clip:padding-box;background-color:#fff;border:1px solid #ccc;border-radius:4px;box-shadow:0 1px 5px #ccc;display:inline-block;font-size:12px;line-height:1.5;list-style:none;outline:none;position:relative;text-align:left}.rc-time-picker-panel-narrow{max-width:113px}.rc-time-picker-panel-input{border:1px solid transparent;cursor:auto;line-height:1.5;margin:0;outline:0;padding:0;width:100%}.rc-time-picker-panel-input-wrap{border-bottom:1px solid #e9e9e9;box-sizing:border-box;padding:6px;position:relative}.rc-time-picker-panel-input-invalid{border-color:red}.rc-time-picker-panel-select{border:1px solid #e9e9e9;border-width:0 1px;box-sizing:border-box;float:left;font-size:12px;margin-left:-1px;max-height:144px;overflow-y:auto;position:relative;width:56px}.rc-time-picker-panel-select-active{overflow-y:auto}.rc-time-picker-panel-select:first-child{border-left:0;margin-left:0}.rc-time-picker-panel-select:last-child{border-right:0}.rc-time-picker-panel-select ul{box-sizing:border-box;list-style:none;margin:0;padding:0;width:100%}.rc-time-picker-panel-select li{cursor:pointer;height:24px;line-height:24px;list-style:none;margin:0;padding:0 0 0 16px;text-align:left;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:100%}.rc-time-picker-panel-select li:hover{background:#edfaff}li.rc-time-picker-panel-select-option-selected{background:#f7f7f7;font-weight:700}li.rc-time-picker-panel-select-option-disabled{color:#ccc}li.rc-time-picker-panel-select-option-disabled:hover{background:transparent;cursor:not-allowed}.customTPicker{width:100%}.customTPicker .rc-time-picker-input{border:1px solid #dedede;border-radius:4px;font-size:1rem;padding:8.8px;width:100%}.customTPicker .rc-time-picker-input:focus{border-color:#f45e29;outline:none}@media (max-width:768px){.customTPicker .rc-time-picker-input{font-size:.9rem;padding:5px 10px}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.rc-time-picker{display:inline-block;position:relative}.rc-time-picker,.rc-time-picker *{box-sizing:border-box}.rc-time-picker-clear{cursor:pointer;height:20px;line-height:20px;margin:0;overflow:hidden;position:absolute;right:6px;text-align:center;top:3px;width:20px}.rc-time-picker-clear-icon:after{color:#aaa;content:"x";display:inline-block;font-size:12px;font-style:normal;height:20px;line-height:1;-webkit-transition:color .3s ease;transition:color .3s ease;width:20px}.rc-time-picker-clear-icon:hover:after{color:#666}.rc-time-picker-input{background-color:#fff;background-image:none;border:1px solid #d9d9d9;border-radius:4px;color:#666;cursor:text;display:inline-block;font-size:12px;height:28px;line-height:1.5;padding:4px 7px;position:relative;-webkit-transition:border .2s cubic-bezier(.645,.045,.355,1),background .2s cubic-bezier(.645,.045,.355,1),box-shadow .2s cubic-bezier(.645,.045,.355,1);transition:border .2s cubic-bezier(.645,.045,.355,1),background .2s cubic-bezier(.645,.045,.355,1),box-shadow .2s cubic-bezier(.645,.045,.355,1);width:100%}.rc-time-picker-input[disabled]{background:#f7f7f7;color:#ccc;cursor:not-allowed}.rc-time-picker-panel{box-sizing:border-box;position:absolute;width:170px;z-index:1070}.rc-time-picker-panel *{box-sizing:border-box}.rc-time-picker-panel-inner{background-clip:padding-box;background-color:#fff;border:1px solid #ccc;border-radius:4px;box-shadow:0 1px 5px #ccc;display:inline-block;font-size:12px;line-height:1.5;list-style:none;outline:none;position:relative;text-align:left}.rc-time-picker-panel-narrow{max-width:113px}.rc-time-picker-panel-input{border:1px solid transparent;cursor:auto;line-height:1.5;margin:0;outline:0;padding:0;width:100%}.rc-time-picker-panel-input-wrap{border-bottom:1px solid #e9e9e9;box-sizing:border-box;padding:6px;position:relative}.rc-time-picker-panel-input-invalid{border-color:red}.rc-time-picker-panel-select{border:1px solid #e9e9e9;border-width:0 1px;box-sizing:border-box;float:left;font-size:12px;margin-left:-1px;max-height:144px;overflow-y:auto;position:relative;width:56px}.rc-time-picker-panel-select-active{overflow-y:auto}.rc-time-picker-panel-select:first-child{border-left:0;margin-left:0}.rc-time-picker-panel-select:last-child{border-right:0}.rc-time-picker-panel-select ul{box-sizing:border-box;list-style:none;margin:0;padding:0;width:100%}.rc-time-picker-panel-select li{cursor:pointer;height:24px;line-height:24px;list-style:none;margin:0;padding:0 0 0 16px;text-align:left;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:100%}.rc-time-picker-panel-select li:hover{background:#edfaff}li.rc-time-picker-panel-select-option-selected{background:#f7f7f7;font-weight:700}li.rc-time-picker-panel-select-option-disabled{color:#ccc}li.rc-time-picker-panel-select-option-disabled:hover{background:transparent;cursor:not-allowed}.customTPicker{width:100%}.customTPicker .rc-time-picker-input{border:1px solid #dedede;border-radius:4px;font-size:1rem;padding:8.8px;width:100%}.customTPicker .rc-time-picker-input:focus{border-color:#f45e29;outline:none}@media (max-width:768px){.customTPicker .rc-time-picker-input{font-size:.9rem;padding:5px 10px}}
|