@visns-studio/visns-components 5.11.3 → 5.11.5
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 +915 -11
- package/package.json +1 -1
- package/src/components/crm/AssociationManager.jsx +151 -33
- package/src/components/crm/BusinessCardOcr.jsx +190 -6
- package/src/components/crm/Field.jsx +27 -0
- package/src/components/crm/Navigation.jsx +65 -5
- package/src/components/crm/generic/GenericReport.jsx +1457 -434
- package/src/components/crm/generic/styles/GenericReport.module.scss +598 -24
- package/src/components/crm/styles/BusinessCardOcr.module.scss +362 -94
- package/src/components/crm/styles/Navigation.module.scss +95 -23
package/README.md
CHANGED
|
@@ -951,6 +951,766 @@ File upload component with drag and drop support.
|
|
|
951
951
|
/>
|
|
952
952
|
```
|
|
953
953
|
|
|
954
|
+
### Association Management Components
|
|
955
|
+
|
|
956
|
+
#### AssociationManager
|
|
957
|
+
|
|
958
|
+
A sophisticated component for managing entity relationships and associations within your application. The AssociationManager provides an intuitive interface for creating, viewing, and managing complex many-to-many relationships between different data entities.
|
|
959
|
+
|
|
960
|
+
```jsx
|
|
961
|
+
<AssociationManager
|
|
962
|
+
config={{
|
|
963
|
+
title: "Client Associations",
|
|
964
|
+
description: "Manage relationships between clients and projects",
|
|
965
|
+
primaryEntity: {
|
|
966
|
+
id: "clients",
|
|
967
|
+
label: "Clients",
|
|
968
|
+
displayKey: "name",
|
|
969
|
+
fetchUrl: "/api/clients"
|
|
970
|
+
},
|
|
971
|
+
secondaryEntity: {
|
|
972
|
+
id: "projects",
|
|
973
|
+
label: "Projects",
|
|
974
|
+
displayKey: "title",
|
|
975
|
+
fetchUrl: "/api/projects"
|
|
976
|
+
},
|
|
977
|
+
association: {
|
|
978
|
+
fetchUrl: "/api/client-project-associations",
|
|
979
|
+
createUrl: "/api/client-project-associations",
|
|
980
|
+
deleteUrl: "/api/client-project-associations",
|
|
981
|
+
pivotTable: "client_project"
|
|
982
|
+
},
|
|
983
|
+
permissions: {
|
|
984
|
+
canCreate: true,
|
|
985
|
+
canDelete: true,
|
|
986
|
+
canView: true
|
|
987
|
+
},
|
|
988
|
+
ui: {
|
|
989
|
+
searchPlaceholder: "Search clients or projects...",
|
|
990
|
+
emptyStateMessage: "No associations found",
|
|
991
|
+
confirmDeleteMessage: "Are you sure you want to remove this association?"
|
|
992
|
+
}
|
|
993
|
+
}}
|
|
994
|
+
userProfile={userProfile}
|
|
995
|
+
onAssociationChange={(data) => console.log('Association changed:', data)}
|
|
996
|
+
/>
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
**Key Features:**
|
|
1000
|
+
|
|
1001
|
+
1. **Bidirectional Relationship Management**:
|
|
1002
|
+
- Create associations from either entity perspective
|
|
1003
|
+
- Automatic relationship synchronization
|
|
1004
|
+
- Real-time updates across related views
|
|
1005
|
+
|
|
1006
|
+
2. **Advanced Search and Filtering**:
|
|
1007
|
+
- Multi-entity search capabilities
|
|
1008
|
+
- Filter by entity type, status, or custom attributes
|
|
1009
|
+
- Debounced search to optimize performance
|
|
1010
|
+
|
|
1011
|
+
3. **Bulk Operations**:
|
|
1012
|
+
- Select multiple entities for batch association
|
|
1013
|
+
- Bulk delete with confirmation dialogs
|
|
1014
|
+
- Mass import/export functionality
|
|
1015
|
+
|
|
1016
|
+
4. **Visual Association Mapping**:
|
|
1017
|
+
- Interactive relationship visualization
|
|
1018
|
+
- Drag-and-drop association creation
|
|
1019
|
+
- Color-coded entity types
|
|
1020
|
+
|
|
1021
|
+
5. **Permission-Based Access Control**:
|
|
1022
|
+
- Granular permissions for create, read, update, delete
|
|
1023
|
+
- Role-based access to specific entity types
|
|
1024
|
+
- Custom permission validation
|
|
1025
|
+
|
|
1026
|
+
**Configuration Options:**
|
|
1027
|
+
|
|
1028
|
+
```jsx
|
|
1029
|
+
const associationConfig = {
|
|
1030
|
+
// Required: Basic entity configuration
|
|
1031
|
+
primaryEntity: {
|
|
1032
|
+
id: "clients", // Unique identifier
|
|
1033
|
+
label: "Clients", // Display name
|
|
1034
|
+
displayKey: "name", // Field to show in lists
|
|
1035
|
+
fetchUrl: "/api/clients", // API endpoint
|
|
1036
|
+
searchFields: ["name", "email"], // Fields to search
|
|
1037
|
+
icon: "Users", // Lucide icon name
|
|
1038
|
+
color: "#3b82f6" // Brand color
|
|
1039
|
+
},
|
|
1040
|
+
|
|
1041
|
+
secondaryEntity: {
|
|
1042
|
+
id: "projects",
|
|
1043
|
+
label: "Projects",
|
|
1044
|
+
displayKey: "title",
|
|
1045
|
+
fetchUrl: "/api/projects",
|
|
1046
|
+
searchFields: ["title", "description"],
|
|
1047
|
+
icon: "FolderOpen",
|
|
1048
|
+
color: "#10b981"
|
|
1049
|
+
},
|
|
1050
|
+
|
|
1051
|
+
// Association configuration
|
|
1052
|
+
association: {
|
|
1053
|
+
fetchUrl: "/api/associations", // Get existing associations
|
|
1054
|
+
createUrl: "/api/associations", // Create new associations
|
|
1055
|
+
updateUrl: "/api/associations", // Update associations (optional)
|
|
1056
|
+
deleteUrl: "/api/associations", // Delete associations
|
|
1057
|
+
pivotTable: "client_project", // Database table name
|
|
1058
|
+
|
|
1059
|
+
// Additional pivot data
|
|
1060
|
+
pivotFields: [
|
|
1061
|
+
{
|
|
1062
|
+
name: "role",
|
|
1063
|
+
label: "Role",
|
|
1064
|
+
type: "select",
|
|
1065
|
+
options: [
|
|
1066
|
+
{ value: "manager", label: "Manager" },
|
|
1067
|
+
{ value: "contributor", label: "Contributor" }
|
|
1068
|
+
]
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
name: "start_date",
|
|
1072
|
+
label: "Start Date",
|
|
1073
|
+
type: "date"
|
|
1074
|
+
}
|
|
1075
|
+
]
|
|
1076
|
+
},
|
|
1077
|
+
|
|
1078
|
+
// UI customization
|
|
1079
|
+
ui: {
|
|
1080
|
+
layout: "split", // "split", "tabs", "cards"
|
|
1081
|
+
theme: "default", // "default", "compact", "detailed"
|
|
1082
|
+
showEntityCounts: true, // Show association counts
|
|
1083
|
+
showLastModified: true, // Show modification timestamps
|
|
1084
|
+
enableDragDrop: true, // Enable drag-and-drop
|
|
1085
|
+
confirmActions: true, // Show confirmation dialogs
|
|
1086
|
+
|
|
1087
|
+
// Custom labels and messages
|
|
1088
|
+
labels: {
|
|
1089
|
+
createButton: "Add Association",
|
|
1090
|
+
deleteButton: "Remove",
|
|
1091
|
+
searchPlaceholder: "Search entities...",
|
|
1092
|
+
noResultsMessage: "No entities found",
|
|
1093
|
+
loadingMessage: "Loading associations..."
|
|
1094
|
+
}
|
|
1095
|
+
},
|
|
1096
|
+
|
|
1097
|
+
// Validation rules
|
|
1098
|
+
validation: {
|
|
1099
|
+
preventDuplicates: true, // Prevent duplicate associations
|
|
1100
|
+
requirePivotData: false, // Require additional pivot fields
|
|
1101
|
+
customValidation: (primary, secondary, pivotData) => {
|
|
1102
|
+
// Custom validation logic
|
|
1103
|
+
return { valid: true, message: "" };
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
|
|
1107
|
+
// Performance optimization
|
|
1108
|
+
performance: {
|
|
1109
|
+
enableVirtualization: true, // For large datasets
|
|
1110
|
+
pageSize: 50, // Pagination size
|
|
1111
|
+
debounceMs: 300, // Search debounce
|
|
1112
|
+
cacheResults: true // Cache API responses
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1115
|
+
```
|
|
1116
|
+
|
|
1117
|
+
**API Integration:**
|
|
1118
|
+
|
|
1119
|
+
The AssociationManager expects specific API response formats:
|
|
1120
|
+
|
|
1121
|
+
```javascript
|
|
1122
|
+
// GET /api/clients (Primary Entity)
|
|
1123
|
+
{
|
|
1124
|
+
"data": [
|
|
1125
|
+
{
|
|
1126
|
+
"id": 1,
|
|
1127
|
+
"name": "Acme Corp",
|
|
1128
|
+
"email": "contact@acme.com",
|
|
1129
|
+
"created_at": "2023-01-01T00:00:00Z"
|
|
1130
|
+
}
|
|
1131
|
+
],
|
|
1132
|
+
"total": 150,
|
|
1133
|
+
"page": 1,
|
|
1134
|
+
"per_page": 50
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// GET /api/associations (Existing Associations)
|
|
1138
|
+
{
|
|
1139
|
+
"data": [
|
|
1140
|
+
{
|
|
1141
|
+
"id": 1,
|
|
1142
|
+
"primary_id": 1,
|
|
1143
|
+
"secondary_id": 5,
|
|
1144
|
+
"pivot_data": {
|
|
1145
|
+
"role": "manager",
|
|
1146
|
+
"start_date": "2023-01-01"
|
|
1147
|
+
},
|
|
1148
|
+
"primary": {
|
|
1149
|
+
"id": 1,
|
|
1150
|
+
"name": "Acme Corp"
|
|
1151
|
+
},
|
|
1152
|
+
"secondary": {
|
|
1153
|
+
"id": 5,
|
|
1154
|
+
"title": "Website Redesign"
|
|
1155
|
+
},
|
|
1156
|
+
"created_at": "2023-01-01T00:00:00Z"
|
|
1157
|
+
}
|
|
1158
|
+
]
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// POST /api/associations (Create Association)
|
|
1162
|
+
{
|
|
1163
|
+
"primary_id": 1,
|
|
1164
|
+
"secondary_id": 5,
|
|
1165
|
+
"pivot_data": {
|
|
1166
|
+
"role": "manager",
|
|
1167
|
+
"start_date": "2023-01-01"
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// Response:
|
|
1172
|
+
{
|
|
1173
|
+
"success": true,
|
|
1174
|
+
"data": {
|
|
1175
|
+
"id": 1,
|
|
1176
|
+
"primary_id": 1,
|
|
1177
|
+
"secondary_id": 5,
|
|
1178
|
+
"created_at": "2023-01-01T00:00:00Z"
|
|
1179
|
+
},
|
|
1180
|
+
"message": "Association created successfully"
|
|
1181
|
+
}
|
|
1182
|
+
```
|
|
1183
|
+
|
|
1184
|
+
**Event Handling:**
|
|
1185
|
+
|
|
1186
|
+
```jsx
|
|
1187
|
+
<AssociationManager
|
|
1188
|
+
config={associationConfig}
|
|
1189
|
+
onAssociationChange={(event) => {
|
|
1190
|
+
// event.type: 'created', 'updated', 'deleted'
|
|
1191
|
+
// event.data: Association data
|
|
1192
|
+
// event.entities: Affected entities
|
|
1193
|
+
console.log('Association event:', event);
|
|
1194
|
+
}}
|
|
1195
|
+
onEntitySelect={(entity, type) => {
|
|
1196
|
+
// Handle entity selection
|
|
1197
|
+
console.log('Entity selected:', entity, type);
|
|
1198
|
+
}}
|
|
1199
|
+
onError={(error) => {
|
|
1200
|
+
// Handle errors
|
|
1201
|
+
console.error('Association error:', error);
|
|
1202
|
+
}}
|
|
1203
|
+
/>
|
|
1204
|
+
```
|
|
1205
|
+
|
|
1206
|
+
**Advanced Usage Examples:**
|
|
1207
|
+
|
|
1208
|
+
1. **Client-Project Association with Roles**:
|
|
1209
|
+
```jsx
|
|
1210
|
+
// Managing client assignments to projects with specific roles
|
|
1211
|
+
<AssociationManager
|
|
1212
|
+
config={{
|
|
1213
|
+
primaryEntity: { id: "clients", label: "Clients", ... },
|
|
1214
|
+
secondaryEntity: { id: "projects", label: "Projects", ... },
|
|
1215
|
+
association: {
|
|
1216
|
+
pivotFields: [
|
|
1217
|
+
{
|
|
1218
|
+
name: "role",
|
|
1219
|
+
label: "Role in Project",
|
|
1220
|
+
type: "select",
|
|
1221
|
+
required: true,
|
|
1222
|
+
options: [
|
|
1223
|
+
{ value: "sponsor", label: "Project Sponsor" },
|
|
1224
|
+
{ value: "stakeholder", label: "Key Stakeholder" },
|
|
1225
|
+
{ value: "user", label: "End User" }
|
|
1226
|
+
]
|
|
1227
|
+
}
|
|
1228
|
+
]
|
|
1229
|
+
}
|
|
1230
|
+
}}
|
|
1231
|
+
/>
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
2. **User-Permission Association**:
|
|
1235
|
+
```jsx
|
|
1236
|
+
// Managing user permissions with expiration dates
|
|
1237
|
+
<AssociationManager
|
|
1238
|
+
config={{
|
|
1239
|
+
primaryEntity: { id: "users", label: "Users", ... },
|
|
1240
|
+
secondaryEntity: { id: "permissions", label: "Permissions", ... },
|
|
1241
|
+
association: {
|
|
1242
|
+
pivotFields: [
|
|
1243
|
+
{
|
|
1244
|
+
name: "expires_at",
|
|
1245
|
+
label: "Expiration Date",
|
|
1246
|
+
type: "datetime",
|
|
1247
|
+
required: false
|
|
1248
|
+
},
|
|
1249
|
+
{
|
|
1250
|
+
name: "granted_by",
|
|
1251
|
+
label: "Granted By",
|
|
1252
|
+
type: "select",
|
|
1253
|
+
fetchUrl: "/api/users/managers"
|
|
1254
|
+
}
|
|
1255
|
+
]
|
|
1256
|
+
}
|
|
1257
|
+
}}
|
|
1258
|
+
/>
|
|
1259
|
+
```
|
|
1260
|
+
|
|
1261
|
+
### Business Card OCR Component
|
|
1262
|
+
|
|
1263
|
+
#### BusinessCardOcr (Enhanced v5.11.1+)
|
|
1264
|
+
|
|
1265
|
+
An advanced OCR (Optical Character Recognition) component specifically designed for scanning business cards and automatically extracting contact information. The component provides intelligent text recognition, client matching, and seamless contact management integration.
|
|
1266
|
+
|
|
1267
|
+
```jsx
|
|
1268
|
+
<BusinessCardOcr
|
|
1269
|
+
isOpen={showScanner}
|
|
1270
|
+
onClose={() => setShowScanner(false)}
|
|
1271
|
+
onContactSaved={(contact) => {
|
|
1272
|
+
console.log('Contact saved:', contact);
|
|
1273
|
+
// Handle the saved contact data
|
|
1274
|
+
}}
|
|
1275
|
+
fields={[
|
|
1276
|
+
{ id: 'name', label: 'Full Name', required: true },
|
|
1277
|
+
{ id: 'email', label: 'Email', type: 'email' },
|
|
1278
|
+
{ id: 'phone', label: 'Phone', type: 'tel' },
|
|
1279
|
+
{ id: 'company', label: 'Company' },
|
|
1280
|
+
{ id: 'position', label: 'Position' },
|
|
1281
|
+
{ id: 'address', label: 'Address', type: 'textarea' }
|
|
1282
|
+
]}
|
|
1283
|
+
fetchConfig={{
|
|
1284
|
+
ocrUrl: '/api/ocr/business-card',
|
|
1285
|
+
saveUrl: '/api/contacts',
|
|
1286
|
+
clientSearchUrl: '/api/clients/search',
|
|
1287
|
+
duplicateCheckUrl: '/api/contacts/check-duplicate'
|
|
1288
|
+
}}
|
|
1289
|
+
settings={{
|
|
1290
|
+
enableClientMatching: true,
|
|
1291
|
+
enableDuplicateCheck: true,
|
|
1292
|
+
autoSave: false,
|
|
1293
|
+
imageQuality: 0.8,
|
|
1294
|
+
supportedFormats: ['image/jpeg', 'image/png', 'image/webp']
|
|
1295
|
+
}}
|
|
1296
|
+
/>
|
|
1297
|
+
```
|
|
1298
|
+
|
|
1299
|
+
**Key Features:**
|
|
1300
|
+
|
|
1301
|
+
1. **Advanced OCR Processing**:
|
|
1302
|
+
- High-accuracy text extraction from business card images
|
|
1303
|
+
- Intelligent field mapping and recognition
|
|
1304
|
+
- Support for multiple languages and formats
|
|
1305
|
+
- Automatic rotation and perspective correction
|
|
1306
|
+
|
|
1307
|
+
2. **Smart Client Matching**:
|
|
1308
|
+
- Automatic company name recognition
|
|
1309
|
+
- Fuzzy matching against existing client database
|
|
1310
|
+
- Confidence scoring for match suggestions
|
|
1311
|
+
- Manual override and confirmation options
|
|
1312
|
+
|
|
1313
|
+
3. **Duplicate Detection**:
|
|
1314
|
+
- Email and phone number duplicate checking
|
|
1315
|
+
- Visual comparison with existing contacts
|
|
1316
|
+
- Merge or create new contact options
|
|
1317
|
+
- Conflict resolution interface
|
|
1318
|
+
|
|
1319
|
+
4. **Interactive Editing Interface**:
|
|
1320
|
+
- Real-time field editing and validation
|
|
1321
|
+
- Drag-and-drop image upload
|
|
1322
|
+
- Live camera capture (mobile devices)
|
|
1323
|
+
- Field confidence indicators
|
|
1324
|
+
|
|
1325
|
+
5. **Contact Management Integration**:
|
|
1326
|
+
- Direct save to contact database
|
|
1327
|
+
- Custom field mapping and validation
|
|
1328
|
+
- Bulk import capabilities
|
|
1329
|
+
- Export to various formats
|
|
1330
|
+
|
|
1331
|
+
**Configuration Options:**
|
|
1332
|
+
|
|
1333
|
+
```jsx
|
|
1334
|
+
const businessCardConfig = {
|
|
1335
|
+
// Field definitions for extracted data
|
|
1336
|
+
fields: [
|
|
1337
|
+
{
|
|
1338
|
+
id: 'name',
|
|
1339
|
+
label: 'Full Name',
|
|
1340
|
+
type: 'text',
|
|
1341
|
+
required: true,
|
|
1342
|
+
validation: {
|
|
1343
|
+
minLength: 2,
|
|
1344
|
+
pattern: /^[a-zA-Z\s]+$/
|
|
1345
|
+
},
|
|
1346
|
+
ocrMapping: ['name', 'full_name', 'contact_name']
|
|
1347
|
+
},
|
|
1348
|
+
{
|
|
1349
|
+
id: 'email',
|
|
1350
|
+
label: 'Email Address',
|
|
1351
|
+
type: 'email',
|
|
1352
|
+
required: false,
|
|
1353
|
+
validation: {
|
|
1354
|
+
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
1355
|
+
},
|
|
1356
|
+
ocrMapping: ['email', 'e_mail', 'email_address']
|
|
1357
|
+
},
|
|
1358
|
+
{
|
|
1359
|
+
id: 'phone',
|
|
1360
|
+
label: 'Phone Number',
|
|
1361
|
+
type: 'tel',
|
|
1362
|
+
required: false,
|
|
1363
|
+
formatting: {
|
|
1364
|
+
mask: '(000) 000-0000',
|
|
1365
|
+
international: true
|
|
1366
|
+
},
|
|
1367
|
+
ocrMapping: ['phone', 'mobile', 'telephone', 'cell']
|
|
1368
|
+
},
|
|
1369
|
+
{
|
|
1370
|
+
id: 'company',
|
|
1371
|
+
label: 'Company Name',
|
|
1372
|
+
type: 'text',
|
|
1373
|
+
required: false,
|
|
1374
|
+
clientMatching: true, // Enable client matching for this field
|
|
1375
|
+
ocrMapping: ['company', 'organization', 'business_name']
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
id: 'position',
|
|
1379
|
+
label: 'Job Title',
|
|
1380
|
+
type: 'text',
|
|
1381
|
+
required: false,
|
|
1382
|
+
ocrMapping: ['title', 'position', 'job_title', 'role']
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
id: 'address',
|
|
1386
|
+
label: 'Address',
|
|
1387
|
+
type: 'textarea',
|
|
1388
|
+
required: false,
|
|
1389
|
+
formatting: {
|
|
1390
|
+
multiline: true,
|
|
1391
|
+
rows: 3
|
|
1392
|
+
},
|
|
1393
|
+
ocrMapping: ['address', 'location', 'street_address']
|
|
1394
|
+
},
|
|
1395
|
+
{
|
|
1396
|
+
id: 'website',
|
|
1397
|
+
label: 'Website',
|
|
1398
|
+
type: 'url',
|
|
1399
|
+
required: false,
|
|
1400
|
+
validation: {
|
|
1401
|
+
pattern: /^https?:\/\/.+/
|
|
1402
|
+
},
|
|
1403
|
+
ocrMapping: ['website', 'url', 'web', 'site']
|
|
1404
|
+
}
|
|
1405
|
+
],
|
|
1406
|
+
|
|
1407
|
+
// API endpoint configuration
|
|
1408
|
+
fetchConfig: {
|
|
1409
|
+
ocrUrl: '/api/ocr/business-card', // OCR processing endpoint
|
|
1410
|
+
saveUrl: '/api/contacts', // Contact save endpoint
|
|
1411
|
+
clientSearchUrl: '/api/clients/search', // Client matching endpoint
|
|
1412
|
+
duplicateCheckUrl: '/api/contacts/check', // Duplicate check endpoint
|
|
1413
|
+
uploadUrl: '/api/files/upload', // Image upload endpoint
|
|
1414
|
+
|
|
1415
|
+
// Request configuration
|
|
1416
|
+
timeout: 30000, // 30 second timeout
|
|
1417
|
+
retries: 3, // Retry failed requests
|
|
1418
|
+
headers: {
|
|
1419
|
+
'Accept': 'application/json',
|
|
1420
|
+
'X-Requested-With': 'XMLHttpRequest'
|
|
1421
|
+
}
|
|
1422
|
+
},
|
|
1423
|
+
|
|
1424
|
+
// Component behavior settings
|
|
1425
|
+
settings: {
|
|
1426
|
+
// OCR settings
|
|
1427
|
+
imageQuality: 0.8, // Image compression quality
|
|
1428
|
+
maxImageSize: 5242880, // 5MB max file size
|
|
1429
|
+
supportedFormats: [ // Accepted image formats
|
|
1430
|
+
'image/jpeg',
|
|
1431
|
+
'image/png',
|
|
1432
|
+
'image/webp',
|
|
1433
|
+
'image/bmp'
|
|
1434
|
+
],
|
|
1435
|
+
|
|
1436
|
+
// Processing options
|
|
1437
|
+
enableClientMatching: true, // Enable company matching
|
|
1438
|
+
enableDuplicateCheck: true, // Check for duplicates
|
|
1439
|
+
autoSave: false, // Auto-save after OCR
|
|
1440
|
+
requireConfirmation: true, // Confirm before saving
|
|
1441
|
+
|
|
1442
|
+
// UI options
|
|
1443
|
+
showConfidenceScores: true, // Show OCR confidence
|
|
1444
|
+
enableManualEdit: true, // Allow manual editing
|
|
1445
|
+
showImagePreview: true, // Show uploaded image
|
|
1446
|
+
enableCameraCapture: true, // Enable camera on mobile
|
|
1447
|
+
|
|
1448
|
+
// Validation options
|
|
1449
|
+
validateOnBlur: true, // Validate fields on blur
|
|
1450
|
+
highlightErrors: true, // Highlight validation errors
|
|
1451
|
+
showTooltips: true, // Show field tooltips
|
|
1452
|
+
|
|
1453
|
+
// Performance options
|
|
1454
|
+
debounceMs: 500, // Debounce for auto-save
|
|
1455
|
+
cacheResults: true, // Cache OCR results
|
|
1456
|
+
enablePreprocessing: true // Image preprocessing
|
|
1457
|
+
},
|
|
1458
|
+
|
|
1459
|
+
// UI customization
|
|
1460
|
+
ui: {
|
|
1461
|
+
theme: 'default', // 'default', 'compact', 'minimal'
|
|
1462
|
+
layout: 'modal', // 'modal', 'inline', 'drawer'
|
|
1463
|
+
size: 'large', // 'small', 'medium', 'large'
|
|
1464
|
+
|
|
1465
|
+
// Labels and messages
|
|
1466
|
+
labels: {
|
|
1467
|
+
uploadButton: 'Upload Business Card',
|
|
1468
|
+
scanButton: 'Scan with Camera',
|
|
1469
|
+
saveButton: 'Save Contact',
|
|
1470
|
+
cancelButton: 'Cancel',
|
|
1471
|
+
editButton: 'Edit Information',
|
|
1472
|
+
processButton: 'Process Image'
|
|
1473
|
+
},
|
|
1474
|
+
|
|
1475
|
+
messages: {
|
|
1476
|
+
uploadPrompt: 'Drop an image here or click to upload',
|
|
1477
|
+
processing: 'Processing business card...',
|
|
1478
|
+
ocrSuccess: 'Text extracted successfully',
|
|
1479
|
+
ocrError: 'Failed to process image',
|
|
1480
|
+
saveSuccess: 'Contact saved successfully',
|
|
1481
|
+
duplicateFound: 'Duplicate contact detected'
|
|
1482
|
+
},
|
|
1483
|
+
|
|
1484
|
+
// Style customization
|
|
1485
|
+
styles: {
|
|
1486
|
+
primaryColor: '#3b82f6',
|
|
1487
|
+
successColor: '#10b981',
|
|
1488
|
+
errorColor: '#ef4444',
|
|
1489
|
+
borderRadius: '8px',
|
|
1490
|
+
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)'
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
};
|
|
1494
|
+
```
|
|
1495
|
+
|
|
1496
|
+
**API Integration:**
|
|
1497
|
+
|
|
1498
|
+
The BusinessCardOcr component expects specific API response formats:
|
|
1499
|
+
|
|
1500
|
+
```javascript
|
|
1501
|
+
// POST /api/ocr/business-card (OCR Processing)
|
|
1502
|
+
// Request: FormData with image file
|
|
1503
|
+
{
|
|
1504
|
+
"image": File
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
// Response:
|
|
1508
|
+
{
|
|
1509
|
+
"success": true,
|
|
1510
|
+
"data": {
|
|
1511
|
+
"extracted_text": "John Smith\nSenior Developer\nAcme Corp\nj.smith@acme.com\n(555) 123-4567",
|
|
1512
|
+
"fields": {
|
|
1513
|
+
"name": {
|
|
1514
|
+
"value": "John Smith",
|
|
1515
|
+
"confidence": 0.95,
|
|
1516
|
+
"position": { "x": 100, "y": 50, "width": 200, "height": 30 }
|
|
1517
|
+
},
|
|
1518
|
+
"email": {
|
|
1519
|
+
"value": "j.smith@acme.com",
|
|
1520
|
+
"confidence": 0.92,
|
|
1521
|
+
"position": { "x": 100, "y": 120, "width": 250, "height": 25 }
|
|
1522
|
+
},
|
|
1523
|
+
"company": {
|
|
1524
|
+
"value": "Acme Corp",
|
|
1525
|
+
"confidence": 0.88,
|
|
1526
|
+
"position": { "x": 100, "y": 80, "width": 150, "height": 25 }
|
|
1527
|
+
}
|
|
1528
|
+
},
|
|
1529
|
+
"processing_time": 2.3,
|
|
1530
|
+
"image_url": "/storage/uploads/card_123.jpg"
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// GET /api/clients/search (Client Matching)
|
|
1535
|
+
{
|
|
1536
|
+
"query": "Acme Corp"
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
// Response:
|
|
1540
|
+
{
|
|
1541
|
+
"data": [
|
|
1542
|
+
{
|
|
1543
|
+
"id": 5,
|
|
1544
|
+
"name": "Acme Corporation",
|
|
1545
|
+
"match_score": 0.85,
|
|
1546
|
+
"address": "123 Business St",
|
|
1547
|
+
"phone": "(555) 987-6543",
|
|
1548
|
+
"website": "https://acme.com"
|
|
1549
|
+
}
|
|
1550
|
+
]
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
// POST /api/contacts/check (Duplicate Check)
|
|
1554
|
+
{
|
|
1555
|
+
"email": "j.smith@acme.com",
|
|
1556
|
+
"phone": "(555) 123-4567"
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
// Response:
|
|
1560
|
+
{
|
|
1561
|
+
"duplicates": [
|
|
1562
|
+
{
|
|
1563
|
+
"id": 10,
|
|
1564
|
+
"name": "John Smith",
|
|
1565
|
+
"email": "j.smith@acme.com",
|
|
1566
|
+
"match_type": "email",
|
|
1567
|
+
"match_score": 1.0,
|
|
1568
|
+
"created_at": "2023-01-01T00:00:00Z"
|
|
1569
|
+
}
|
|
1570
|
+
]
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
// POST /api/contacts (Save Contact)
|
|
1574
|
+
{
|
|
1575
|
+
"name": "John Smith",
|
|
1576
|
+
"email": "j.smith@acme.com",
|
|
1577
|
+
"phone": "(555) 123-4567",
|
|
1578
|
+
"company": "Acme Corp",
|
|
1579
|
+
"position": "Senior Developer",
|
|
1580
|
+
"client_id": 5,
|
|
1581
|
+
"source": "business_card_ocr",
|
|
1582
|
+
"confidence_scores": {
|
|
1583
|
+
"name": 0.95,
|
|
1584
|
+
"email": 0.92,
|
|
1585
|
+
"company": 0.88
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// Response:
|
|
1590
|
+
{
|
|
1591
|
+
"success": true,
|
|
1592
|
+
"data": {
|
|
1593
|
+
"id": 15,
|
|
1594
|
+
"name": "John Smith",
|
|
1595
|
+
"created_at": "2023-01-01T00:00:00Z"
|
|
1596
|
+
},
|
|
1597
|
+
"message": "Contact created successfully"
|
|
1598
|
+
}
|
|
1599
|
+
```
|
|
1600
|
+
|
|
1601
|
+
**Event Handling:**
|
|
1602
|
+
|
|
1603
|
+
```jsx
|
|
1604
|
+
<BusinessCardOcr
|
|
1605
|
+
isOpen={showScanner}
|
|
1606
|
+
onClose={() => setShowScanner(false)}
|
|
1607
|
+
onContactSaved={(contact) => {
|
|
1608
|
+
// Handle successful contact save
|
|
1609
|
+
console.log('New contact saved:', contact);
|
|
1610
|
+
|
|
1611
|
+
// Optional: Update parent component state
|
|
1612
|
+
setContacts(prev => [...prev, contact]);
|
|
1613
|
+
|
|
1614
|
+
// Optional: Show success notification
|
|
1615
|
+
toast.success(`Contact ${contact.name} saved successfully`);
|
|
1616
|
+
}}
|
|
1617
|
+
onOcrComplete={(extractedData) => {
|
|
1618
|
+
// Handle OCR completion
|
|
1619
|
+
console.log('OCR extraction completed:', extractedData);
|
|
1620
|
+
|
|
1621
|
+
// Optional: Custom processing of extracted data
|
|
1622
|
+
if (extractedData.confidence < 0.7) {
|
|
1623
|
+
toast.warning('Low confidence OCR results. Please review carefully.');
|
|
1624
|
+
}
|
|
1625
|
+
}}
|
|
1626
|
+
onClientMatch={(matches) => {
|
|
1627
|
+
// Handle client matching results
|
|
1628
|
+
console.log('Client matches found:', matches);
|
|
1629
|
+
|
|
1630
|
+
// Optional: Auto-select high confidence matches
|
|
1631
|
+
if (matches.length === 1 && matches[0].match_score > 0.9) {
|
|
1632
|
+
// Auto-select the best match
|
|
1633
|
+
return matches[0];
|
|
1634
|
+
}
|
|
1635
|
+
}}
|
|
1636
|
+
onDuplicateDetected={(duplicates) => {
|
|
1637
|
+
// Handle duplicate detection
|
|
1638
|
+
console.log('Duplicate contacts found:', duplicates);
|
|
1639
|
+
|
|
1640
|
+
// Optional: Custom duplicate handling logic
|
|
1641
|
+
return new Promise((resolve) => {
|
|
1642
|
+
// Show custom duplicate resolution UI
|
|
1643
|
+
showDuplicateDialog(duplicates, resolve);
|
|
1644
|
+
});
|
|
1645
|
+
}}
|
|
1646
|
+
onError={(error) => {
|
|
1647
|
+
// Handle errors
|
|
1648
|
+
console.error('OCR error:', error);
|
|
1649
|
+
|
|
1650
|
+
// Optional: Custom error handling
|
|
1651
|
+
if (error.type === 'network') {
|
|
1652
|
+
toast.error('Network error. Please check your connection.');
|
|
1653
|
+
} else if (error.type === 'validation') {
|
|
1654
|
+
toast.error('Please correct the highlighted fields.');
|
|
1655
|
+
}
|
|
1656
|
+
}}
|
|
1657
|
+
/>
|
|
1658
|
+
```
|
|
1659
|
+
|
|
1660
|
+
**Advanced Usage Examples:**
|
|
1661
|
+
|
|
1662
|
+
1. **Integration with Existing Contact Form**:
|
|
1663
|
+
```jsx
|
|
1664
|
+
const ContactForm = () => {
|
|
1665
|
+
const [showOcr, setShowOcr] = useState(false);
|
|
1666
|
+
const [formData, setFormData] = useState({});
|
|
1667
|
+
|
|
1668
|
+
return (
|
|
1669
|
+
<div>
|
|
1670
|
+
<Form data={formData} onChange={setFormData}>
|
|
1671
|
+
{/* Regular form fields */}
|
|
1672
|
+
</Form>
|
|
1673
|
+
|
|
1674
|
+
<button onClick={() => setShowOcr(true)}>
|
|
1675
|
+
Scan Business Card
|
|
1676
|
+
</button>
|
|
1677
|
+
|
|
1678
|
+
<BusinessCardOcr
|
|
1679
|
+
isOpen={showOcr}
|
|
1680
|
+
onClose={() => setShowOcr(false)}
|
|
1681
|
+
onContactSaved={(contact) => {
|
|
1682
|
+
// Pre-fill form with OCR data
|
|
1683
|
+
setFormData(contact);
|
|
1684
|
+
setShowOcr(false);
|
|
1685
|
+
}}
|
|
1686
|
+
/>
|
|
1687
|
+
</div>
|
|
1688
|
+
);
|
|
1689
|
+
};
|
|
1690
|
+
```
|
|
1691
|
+
|
|
1692
|
+
2. **Bulk Business Card Processing**:
|
|
1693
|
+
```jsx
|
|
1694
|
+
const BulkCardProcessor = () => {
|
|
1695
|
+
const [queue, setQueue] = useState([]);
|
|
1696
|
+
|
|
1697
|
+
return (
|
|
1698
|
+
<BusinessCardOcr
|
|
1699
|
+
settings={{
|
|
1700
|
+
autoSave: true,
|
|
1701
|
+
enableBulkMode: true
|
|
1702
|
+
}}
|
|
1703
|
+
onContactSaved={(contact) => {
|
|
1704
|
+
setQueue(prev => [...prev, contact]);
|
|
1705
|
+
}}
|
|
1706
|
+
onBulkComplete={(contacts) => {
|
|
1707
|
+
console.log(`Processed ${contacts.length} business cards`);
|
|
1708
|
+
}}
|
|
1709
|
+
/>
|
|
1710
|
+
);
|
|
1711
|
+
};
|
|
1712
|
+
```
|
|
1713
|
+
|
|
954
1714
|
### Utility Components
|
|
955
1715
|
|
|
956
1716
|
#### CustomFetch
|
|
@@ -1309,9 +2069,9 @@ Response:
|
|
|
1309
2069
|
|
|
1310
2070
|
### Report Builder Component
|
|
1311
2071
|
|
|
1312
|
-
#### GenericReport
|
|
2072
|
+
#### GenericReport (Enhanced v5.10.11+)
|
|
1313
2073
|
|
|
1314
|
-
A powerful report building component that
|
|
2074
|
+
A powerful and enhanced report building component that provides an intuitive wizard-based interface for creating custom reports. The component has been significantly improved with new features and better user experience.
|
|
1315
2075
|
|
|
1316
2076
|
```jsx
|
|
1317
2077
|
<GenericReport
|
|
@@ -1322,19 +2082,108 @@ A powerful report building component that allows users to create custom reports
|
|
|
1322
2082
|
executeUrl: '/ajax/reportBuilder/execute',
|
|
1323
2083
|
suggestedJoinsUrl: '/ajax/reportBuilder/getSuggestedJoins',
|
|
1324
2084
|
}}
|
|
2085
|
+
userProfile={userProfile}
|
|
2086
|
+
businessTemplates={businessTemplates} // Optional pre-built templates
|
|
1325
2087
|
/>
|
|
1326
2088
|
```
|
|
1327
2089
|
|
|
1328
|
-
|
|
2090
|
+
**Major Enhancements:**
|
|
2091
|
+
|
|
2092
|
+
- **5-Step Wizard Interface**: Guided workflow with visual progress indicators
|
|
2093
|
+
- **Business Templates**: Pre-built report templates for common business scenarios
|
|
2094
|
+
- **Calculated Fields**: Create custom formulas and calculations within reports
|
|
2095
|
+
- **Smart Column Selection**: Automatic suggestions based on table relationships
|
|
2096
|
+
- **Enhanced Filtering**: Advanced filter options with AND/OR logic and grouping
|
|
2097
|
+
- **Live Preview**: Real-time preview of report data as you build
|
|
2098
|
+
- **Intelligent Currency Formatting**: Automatic detection and formatting of monetary fields
|
|
2099
|
+
- **Expandable Field Summary**: View selected fields in a collapsible overview
|
|
2100
|
+
- **Template Management**: Save, load, and share report templates
|
|
2101
|
+
- **Multiple Export Formats**: Export to Excel, CSV, PDF with formatting options
|
|
2102
|
+
|
|
2103
|
+
**New Features in Recent Updates:**
|
|
2104
|
+
|
|
2105
|
+
1. **Calculated Fields (v5.10.12+)**:
|
|
2106
|
+
```jsx
|
|
2107
|
+
// Users can create custom calculated fields
|
|
2108
|
+
{
|
|
2109
|
+
id: 'profit_margin',
|
|
2110
|
+
displayName: 'Profit Margin %',
|
|
2111
|
+
formula: 'ROUND(((revenue - cost) / revenue) * 100, 2)',
|
|
2112
|
+
type: 'decimal'
|
|
2113
|
+
}
|
|
2114
|
+
```
|
|
2115
|
+
|
|
2116
|
+
2. **Business Templates (v5.11.0+)**:
|
|
2117
|
+
```jsx
|
|
2118
|
+
// Pre-built templates for common scenarios
|
|
2119
|
+
const businessTemplates = [
|
|
2120
|
+
{
|
|
2121
|
+
id: 'utilization_planning',
|
|
2122
|
+
name: 'Utilization Planning',
|
|
2123
|
+
description: 'Weekly resource allocation and laydown utilization',
|
|
2124
|
+
icon: 'Calendar',
|
|
2125
|
+
mainTable: 'leads',
|
|
2126
|
+
defaultColumns: [...],
|
|
2127
|
+
calculatedFields: [...]
|
|
2128
|
+
}
|
|
2129
|
+
];
|
|
2130
|
+
```
|
|
2131
|
+
|
|
2132
|
+
3. **Smart Currency Detection**:
|
|
2133
|
+
- Automatically formats fields containing 'value', 'price', 'cost', 'amount', etc.
|
|
2134
|
+
- Supports Australian Dollar (AUD) formatting by default
|
|
2135
|
+
- Configurable currency formatting options
|
|
2136
|
+
|
|
2137
|
+
4. **Enhanced UI/UX**:
|
|
2138
|
+
- Lucide React icons throughout for consistency
|
|
2139
|
+
- Larger calculated field display for better visibility
|
|
2140
|
+
- Expandable selected fields summary
|
|
2141
|
+
- Step-by-step guidance with tooltips and help text
|
|
2142
|
+
|
|
2143
|
+
**The 5-Step Wizard Process:**
|
|
2144
|
+
|
|
2145
|
+
1. **Select Data Source**: Choose main table and review business templates
|
|
2146
|
+
2. **Choose Columns**: Select specific data fields with drag-and-drop reordering
|
|
2147
|
+
3. **Add Relationships**: Connect related tables with automatic join suggestions
|
|
2148
|
+
4. **Apply Filters**: Set conditions and date ranges with advanced logic
|
|
2149
|
+
5. **Generate & Export**: Preview results and export in multiple formats
|
|
2150
|
+
|
|
2151
|
+
**API Integration:**
|
|
2152
|
+
|
|
2153
|
+
The component expects these endpoint responses:
|
|
1329
2154
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
2155
|
+
```javascript
|
|
2156
|
+
// Tables endpoint
|
|
2157
|
+
{
|
|
2158
|
+
"tables": [
|
|
2159
|
+
{
|
|
2160
|
+
"id": "clients",
|
|
2161
|
+
"label": "Clients",
|
|
2162
|
+
"description": "Customer information"
|
|
2163
|
+
}
|
|
2164
|
+
]
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
// Columns endpoint
|
|
2168
|
+
{
|
|
2169
|
+
"columns": [
|
|
2170
|
+
{
|
|
2171
|
+
"name": "id",
|
|
2172
|
+
"type": "integer",
|
|
2173
|
+
"nullable": false,
|
|
2174
|
+
"key": "primary"
|
|
2175
|
+
}
|
|
2176
|
+
]
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
// Execute endpoint
|
|
2180
|
+
{
|
|
2181
|
+
"success": true,
|
|
2182
|
+
"data": [...],
|
|
2183
|
+
"total": 150,
|
|
2184
|
+
"query": "SELECT ... FROM ..."
|
|
2185
|
+
}
|
|
2186
|
+
```
|
|
1338
2187
|
|
|
1339
2188
|
### Sketch Field Component
|
|
1340
2189
|
|
|
@@ -1650,6 +2499,61 @@ Modal components support vertical alignment configuration with the `verticalAlig
|
|
|
1650
2499
|
|
|
1651
2500
|
The ReactDataGrid's `defaultGroupBy` prop enables internal grouping functionality that creates collapsible group headers within the table structure. It does NOT create any external UI elements that need relocation, as all grouping controls are internal to the table component.
|
|
1652
2501
|
|
|
2502
|
+
### Navigation Component Icon Visibility Issues
|
|
2503
|
+
|
|
2504
|
+
#### Browser Rendering Optimization Fix
|
|
2505
|
+
|
|
2506
|
+
**Problem:** Navigation action icons (specifically anchor/Link elements) would disappear during page scroll in production environments. The issue only affected anchor tags while button elements remained visible. Notably, the problem disappeared when browser dev tools were open, indicating a browser rendering optimization conflict.
|
|
2507
|
+
|
|
2508
|
+
**Root Cause:** Firefox and other browsers apply aggressive rendering optimizations that can incorrectly cull elements from the paint tree. The combination of CSS `contain: layout` property with sticky positioning was causing anchor elements to be optimized out of view during scroll events.
|
|
2509
|
+
|
|
2510
|
+
**Solution Applied:**
|
|
2511
|
+
|
|
2512
|
+
1. **Removed problematic containment properties:**
|
|
2513
|
+
- Removed `contain: layout` from `.content-header` and action elements
|
|
2514
|
+
- This property was causing layout optimization conflicts
|
|
2515
|
+
|
|
2516
|
+
2. **Enhanced compositor layer creation:**
|
|
2517
|
+
- Changed `transform: translateZ(0)` to `transform: translateZ(0) translateX(0) translateY(0)`
|
|
2518
|
+
- Added `isolation: isolate` to force elements onto their own render layers
|
|
2519
|
+
- Added `backface-visibility: hidden` to prevent rendering glitches
|
|
2520
|
+
|
|
2521
|
+
3. **Improved z-index stacking:**
|
|
2522
|
+
- Elevated z-index values from 100-103 range to 1000-1003 range
|
|
2523
|
+
- Ensures action icons stay above sticky header's stacking context
|
|
2524
|
+
|
|
2525
|
+
**Files Modified:**
|
|
2526
|
+
- `src/components/crm/styles/Navigation.module.scss` (content-header and action styling)
|
|
2527
|
+
- Enhanced both regular and alternate theme action containers
|
|
2528
|
+
|
|
2529
|
+
#### Navigation Component Debug Mode
|
|
2530
|
+
|
|
2531
|
+
The Navigation component now includes optional debugging capabilities to help diagnose rendering issues:
|
|
2532
|
+
|
|
2533
|
+
**Enable Debug Mode:**
|
|
2534
|
+
```jsx
|
|
2535
|
+
<Navigation debugIcons={true} {...otherProps} />
|
|
2536
|
+
```
|
|
2537
|
+
|
|
2538
|
+
**Debug Features:**
|
|
2539
|
+
- **Visual indicators:** Red borders around anchor elements with 'A' labels, blue borders around buttons with 'B' labels
|
|
2540
|
+
- **Console logging:** Detailed scroll event logs showing element positions, visibility, z-index, transforms, and containment properties
|
|
2541
|
+
- **Real-time monitoring:** Tracks icon visibility changes during scroll events
|
|
2542
|
+
|
|
2543
|
+
**Debug CSS Classes:**
|
|
2544
|
+
The component includes `.debug-icons` styles that can be applied for visual debugging:
|
|
2545
|
+
```scss
|
|
2546
|
+
.debug-icons {
|
|
2547
|
+
a, button {
|
|
2548
|
+
border: 2px solid red !important;
|
|
2549
|
+
background: rgba(255, 0, 0, 0.1) !important;
|
|
2550
|
+
// Additional debug styling...
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
```
|
|
2554
|
+
|
|
2555
|
+
This fix ensures navigation icons remain visible during scroll across all browser environments and rendering contexts.
|
|
2556
|
+
|
|
1653
2557
|
## Support
|
|
1654
2558
|
|
|
1655
2559
|
For support, please contact the VISNS Studio team or open an issue on GitHub.
|