@visns-studio/visns-components 5.8.9 → 5.8.11
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/package.json +1 -1
- package/src/components/crm/DataGrid.jsx +11 -0
- package/src/components/crm/generic/GenericIndex.jsx +70 -19
- package/src/components/crm/generic/GenericReport.jsx +153 -17
- package/src/components/crm/generic/styles/GenericIndex.module.scss +27 -0
- package/src/components/crm/generic/styles/GenericReport.module.scss +27 -0
- package/src/components/crm/styles/DataGrid.module.scss +26 -0
package/package.json
CHANGED
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
83
83
|
},
|
|
84
84
|
"name": "@visns-studio/visns-components",
|
|
85
|
-
"version": "5.8.
|
|
85
|
+
"version": "5.8.11",
|
|
86
86
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
87
87
|
"main": "src/index.js",
|
|
88
88
|
"files": [
|
|
@@ -2221,6 +2221,8 @@ const DataGrid = forwardRef(
|
|
|
2221
2221
|
name: column.id,
|
|
2222
2222
|
filterEditor: filterEditor,
|
|
2223
2223
|
filterEditorProps: filterEditorProps,
|
|
2224
|
+
defaultWidth: 120, // Set default width for date columns
|
|
2225
|
+
minWidth: 120, // Set minimum width for date columns
|
|
2224
2226
|
render: ({ data }) => {
|
|
2225
2227
|
if (
|
|
2226
2228
|
data &&
|
|
@@ -2272,6 +2274,8 @@ const DataGrid = forwardRef(
|
|
|
2272
2274
|
name: `${column.id[0]}.${column.id[1]}.`,
|
|
2273
2275
|
filterEditor: filterEditor,
|
|
2274
2276
|
filterEditorProps: filterEditorProps,
|
|
2277
|
+
defaultWidth: 200, // Set default width for daterange columns
|
|
2278
|
+
minWidth: 200, // Set minimum width for daterange columns
|
|
2275
2279
|
render: ({ data }) => {
|
|
2276
2280
|
if (
|
|
2277
2281
|
data &&
|
|
@@ -2316,6 +2320,8 @@ const DataGrid = forwardRef(
|
|
|
2316
2320
|
name: column.id,
|
|
2317
2321
|
filterEditor: filterEditor,
|
|
2318
2322
|
filterEditorProps: filterEditorProps,
|
|
2323
|
+
defaultWidth: 160, // Set default width for datetime columns
|
|
2324
|
+
minWidth: 160, // Set minimum width for datetime columns
|
|
2319
2325
|
render: ({ data }) => {
|
|
2320
2326
|
if (
|
|
2321
2327
|
data &&
|
|
@@ -3511,6 +3517,11 @@ const DataGrid = forwardRef(
|
|
|
3511
3517
|
content = content
|
|
3512
3518
|
.replace(/<p>/g, '')
|
|
3513
3519
|
.replace(/<\/p>/g, '<br>');
|
|
3520
|
+
|
|
3521
|
+
// Ensure <br /> tags are properly handled (convert to <br> for consistency)
|
|
3522
|
+
content = content
|
|
3523
|
+
.replace(/<br \/>/g, '<br>')
|
|
3524
|
+
.replace(/<br\/>/g, '<br>');
|
|
3514
3525
|
}
|
|
3515
3526
|
|
|
3516
3527
|
// If truncate has a value, limit content to specified number of characters
|
|
@@ -312,7 +312,27 @@ const Grid = ({ config, userProfile }) => {
|
|
|
312
312
|
return value;
|
|
313
313
|
}
|
|
314
314
|
return value;
|
|
315
|
+
case 'html':
|
|
316
|
+
// Explicitly parse HTML content
|
|
317
|
+
return parse(String(value));
|
|
318
|
+
case 'richtext':
|
|
319
|
+
// Parse rich text content
|
|
320
|
+
return parse(String(value));
|
|
315
321
|
default:
|
|
322
|
+
// For text values, check if it contains HTML tags
|
|
323
|
+
if (
|
|
324
|
+
typeof value === 'string' &&
|
|
325
|
+
(value.includes('<br') ||
|
|
326
|
+
value.includes('<p>') ||
|
|
327
|
+
value.includes('<div') ||
|
|
328
|
+
value.includes('<span'))
|
|
329
|
+
) {
|
|
330
|
+
// Ensure <br /> tags are properly handled
|
|
331
|
+
const processedValue = String(value)
|
|
332
|
+
.replace(/<br \/>/g, '<br>')
|
|
333
|
+
.replace(/<br\/>/g, '<br>');
|
|
334
|
+
return parse(processedValue);
|
|
335
|
+
}
|
|
316
336
|
return value;
|
|
317
337
|
}
|
|
318
338
|
};
|
|
@@ -1218,22 +1238,39 @@ const Grid = ({ config, userProfile }) => {
|
|
|
1218
1238
|
)}
|
|
1219
1239
|
</span>
|
|
1220
1240
|
) : (
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
{
|
|
1226
|
-
type
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
'number'
|
|
1233
|
-
|
|
1234
|
-
: ''),
|
|
1241
|
+
<span
|
|
1242
|
+
className={
|
|
1243
|
+
styles.cellContent
|
|
1244
|
+
}
|
|
1245
|
+
data-type={
|
|
1246
|
+
header.type ||
|
|
1247
|
+
(typeof row[
|
|
1248
|
+
header
|
|
1249
|
+
.key
|
|
1250
|
+
] ===
|
|
1251
|
+
'number'
|
|
1252
|
+
? 'number'
|
|
1253
|
+
: '')
|
|
1235
1254
|
}
|
|
1236
|
-
|
|
1255
|
+
>
|
|
1256
|
+
{formatCellContent(
|
|
1257
|
+
row[
|
|
1258
|
+
header
|
|
1259
|
+
.key
|
|
1260
|
+
],
|
|
1261
|
+
{
|
|
1262
|
+
type:
|
|
1263
|
+
header.type ||
|
|
1264
|
+
(typeof row[
|
|
1265
|
+
header
|
|
1266
|
+
.key
|
|
1267
|
+
] ===
|
|
1268
|
+
'number'
|
|
1269
|
+
? 'number'
|
|
1270
|
+
: ''),
|
|
1271
|
+
}
|
|
1272
|
+
)}
|
|
1273
|
+
</span>
|
|
1237
1274
|
)
|
|
1238
1275
|
) : header.onClick ? (
|
|
1239
1276
|
<span
|
|
@@ -1256,10 +1293,24 @@ const Grid = ({ config, userProfile }) => {
|
|
|
1256
1293
|
key={`cell-${rowIndex}-${colIndex}`}
|
|
1257
1294
|
className={styles.gridCell}
|
|
1258
1295
|
>
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1296
|
+
<span
|
|
1297
|
+
className={
|
|
1298
|
+
styles.cellContent
|
|
1299
|
+
}
|
|
1300
|
+
data-type={
|
|
1301
|
+
col.type ||
|
|
1302
|
+
(typeof row[
|
|
1303
|
+
col.name
|
|
1304
|
+
] === 'number'
|
|
1305
|
+
? 'number'
|
|
1306
|
+
: '')
|
|
1307
|
+
}
|
|
1308
|
+
>
|
|
1309
|
+
{formatCellContent(
|
|
1310
|
+
row[col.name],
|
|
1311
|
+
col
|
|
1312
|
+
)}
|
|
1313
|
+
</span>
|
|
1263
1314
|
</td>
|
|
1264
1315
|
))}
|
|
1265
1316
|
</tr>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
2
3
|
import { toast } from 'react-toastify';
|
|
3
4
|
import moment from 'moment';
|
|
4
5
|
import ReactDataGrid from '@visns-studio/visns-datagrid-enterprise';
|
|
@@ -582,10 +583,13 @@ const flattenFilterCriteria = (filterCriteria) => {
|
|
|
582
583
|
return [];
|
|
583
584
|
};
|
|
584
585
|
|
|
585
|
-
const GenericReport = ({ setting = {} }) => {
|
|
586
|
+
const GenericReport = ({ setting = {}, definition = null }) => {
|
|
586
587
|
// Extract settings with defaults
|
|
587
588
|
const { tableUrl, columnUrl } = setting;
|
|
588
589
|
|
|
590
|
+
// Ref for tooltip element
|
|
591
|
+
const tooltipRef = React.useRef(null);
|
|
592
|
+
|
|
589
593
|
// Help functions for showing popups
|
|
590
594
|
const showReportBuilderHelp = () => {
|
|
591
595
|
Swal.fire({
|
|
@@ -863,6 +867,81 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
863
867
|
fetchSavedReports();
|
|
864
868
|
}, []);
|
|
865
869
|
|
|
870
|
+
// Effect to handle tooltip positioning
|
|
871
|
+
useEffect(() => {
|
|
872
|
+
// Create tooltip element if it doesn't exist
|
|
873
|
+
if (!tooltipRef.current) {
|
|
874
|
+
const tooltip = document.createElement('div');
|
|
875
|
+
tooltip.className = styles.tableTooltip;
|
|
876
|
+
document.body.appendChild(tooltip);
|
|
877
|
+
tooltipRef.current = tooltip;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// Function to show tooltip
|
|
881
|
+
const showTooltip = (e) => {
|
|
882
|
+
const target = e.target.closest('[data-tooltip]');
|
|
883
|
+
if (!target) return;
|
|
884
|
+
|
|
885
|
+
const tooltip = tooltipRef.current;
|
|
886
|
+
const tooltipText = target.getAttribute('data-tooltip');
|
|
887
|
+
|
|
888
|
+
if (!tooltipText) return;
|
|
889
|
+
|
|
890
|
+
tooltip.textContent = tooltipText;
|
|
891
|
+
tooltip.classList.add(styles.visible);
|
|
892
|
+
|
|
893
|
+
// Calculate position
|
|
894
|
+
const rect = target.getBoundingClientRect();
|
|
895
|
+
const tooltipRect = tooltip.getBoundingClientRect();
|
|
896
|
+
|
|
897
|
+
// Default position above the element
|
|
898
|
+
let top = rect.top - tooltipRect.height - 10;
|
|
899
|
+
let left = rect.left + rect.width / 2 - tooltipRect.width / 2;
|
|
900
|
+
|
|
901
|
+
// Check if tooltip would go off the top of the screen
|
|
902
|
+
if (top < 10) {
|
|
903
|
+
// Position below the element instead
|
|
904
|
+
top = rect.bottom + 10;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// Check if tooltip would go off the left of the screen
|
|
908
|
+
if (left < 10) {
|
|
909
|
+
left = 10;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Check if tooltip would go off the right of the screen
|
|
913
|
+
if (left + tooltipRect.width > window.innerWidth - 10) {
|
|
914
|
+
left = window.innerWidth - tooltipRect.width - 10;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
tooltip.style.top = `${top}px`;
|
|
918
|
+
tooltip.style.left = `${left}px`;
|
|
919
|
+
};
|
|
920
|
+
|
|
921
|
+
// Function to hide tooltip
|
|
922
|
+
const hideTooltip = () => {
|
|
923
|
+
if (tooltipRef.current) {
|
|
924
|
+
tooltipRef.current.classList.remove(styles.visible);
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
|
|
928
|
+
// Add event listeners for tooltip
|
|
929
|
+
document.addEventListener('mouseover', showTooltip);
|
|
930
|
+
document.addEventListener('mouseout', hideTooltip);
|
|
931
|
+
|
|
932
|
+
// Cleanup function
|
|
933
|
+
return () => {
|
|
934
|
+
document.removeEventListener('mouseover', showTooltip);
|
|
935
|
+
document.removeEventListener('mouseout', hideTooltip);
|
|
936
|
+
|
|
937
|
+
// Remove tooltip element when component unmounts
|
|
938
|
+
if (tooltipRef.current) {
|
|
939
|
+
document.body.removeChild(tooltipRef.current);
|
|
940
|
+
tooltipRef.current = null;
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
}, [styles.tableTooltip, styles.visible]);
|
|
944
|
+
|
|
866
945
|
// Fetch table columns when a table is selected
|
|
867
946
|
useEffect(() => {
|
|
868
947
|
if (selectedTable) {
|
|
@@ -943,12 +1022,27 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
943
1022
|
const tablesData = res.data?.data || res.data;
|
|
944
1023
|
|
|
945
1024
|
if (Array.isArray(tablesData)) {
|
|
946
|
-
//
|
|
947
|
-
|
|
1025
|
+
// First filter out the hidden tables and pivot tables (tables with underscore in name)
|
|
1026
|
+
let filteredTables = tablesData.filter(
|
|
948
1027
|
(table) =>
|
|
949
1028
|
!hiddenTables.includes(table.name) &&
|
|
950
1029
|
!table.name.includes('_')
|
|
951
1030
|
);
|
|
1031
|
+
|
|
1032
|
+
// If definition prop is provided, only show tables that are in the definition
|
|
1033
|
+
if (
|
|
1034
|
+
definition &&
|
|
1035
|
+
definition.tables &&
|
|
1036
|
+
definition.tables.length > 0
|
|
1037
|
+
) {
|
|
1038
|
+
const definitionTableIds = definition.tables.map(
|
|
1039
|
+
(t) => t.id
|
|
1040
|
+
);
|
|
1041
|
+
filteredTables = filteredTables.filter((table) =>
|
|
1042
|
+
definitionTableIds.includes(table.name)
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
952
1046
|
setTables(filteredTables);
|
|
953
1047
|
} else {
|
|
954
1048
|
console.error('Invalid tables data format:', tablesData);
|
|
@@ -3461,21 +3555,49 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
3461
3555
|
Loading tables...
|
|
3462
3556
|
</div>
|
|
3463
3557
|
) : tables && tables.length > 0 ? (
|
|
3464
|
-
tables.map((table) =>
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3558
|
+
tables.map((table) => {
|
|
3559
|
+
// Find description from definition if available
|
|
3560
|
+
let description = '';
|
|
3561
|
+
if (definition && definition.tables) {
|
|
3562
|
+
const tableDefinition =
|
|
3563
|
+
definition.tables.find(
|
|
3564
|
+
(t) => t.id === table.name
|
|
3565
|
+
);
|
|
3566
|
+
if (tableDefinition) {
|
|
3567
|
+
description =
|
|
3568
|
+
tableDefinition.description || '';
|
|
3474
3569
|
}
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3570
|
+
}
|
|
3571
|
+
|
|
3572
|
+
return (
|
|
3573
|
+
<div
|
|
3574
|
+
key={table.name}
|
|
3575
|
+
className={`${styles.tableItem} ${
|
|
3576
|
+
selectedTable === table.name
|
|
3577
|
+
? styles.tableItemSelected
|
|
3578
|
+
: ''
|
|
3579
|
+
}`}
|
|
3580
|
+
onClick={() =>
|
|
3581
|
+
handleTableSelect(table.name)
|
|
3582
|
+
}
|
|
3583
|
+
data-tooltip={description}
|
|
3584
|
+
>
|
|
3585
|
+
{definition && definition.tables
|
|
3586
|
+
? (() => {
|
|
3587
|
+
const tableDefinition =
|
|
3588
|
+
definition.tables.find(
|
|
3589
|
+
(t) =>
|
|
3590
|
+
t.id ===
|
|
3591
|
+
table.name
|
|
3592
|
+
);
|
|
3593
|
+
return tableDefinition
|
|
3594
|
+
? tableDefinition.label
|
|
3595
|
+
: formatName(table.name);
|
|
3596
|
+
})()
|
|
3597
|
+
: formatName(table.name)}
|
|
3598
|
+
</div>
|
|
3599
|
+
);
|
|
3600
|
+
})
|
|
3479
3601
|
) : (
|
|
3480
3602
|
<div className={styles.emptyMessage}>
|
|
3481
3603
|
No tables available
|
|
@@ -6074,4 +6196,18 @@ const GenericReport = ({ setting = {} }) => {
|
|
|
6074
6196
|
);
|
|
6075
6197
|
};
|
|
6076
6198
|
|
|
6199
|
+
// PropTypes
|
|
6200
|
+
GenericReport.propTypes = {
|
|
6201
|
+
setting: PropTypes.object,
|
|
6202
|
+
definition: PropTypes.shape({
|
|
6203
|
+
tables: PropTypes.arrayOf(
|
|
6204
|
+
PropTypes.shape({
|
|
6205
|
+
id: PropTypes.string.isRequired,
|
|
6206
|
+
label: PropTypes.string.isRequired,
|
|
6207
|
+
description: PropTypes.string,
|
|
6208
|
+
})
|
|
6209
|
+
),
|
|
6210
|
+
}),
|
|
6211
|
+
};
|
|
6212
|
+
|
|
6077
6213
|
export default GenericReport;
|
|
@@ -508,6 +508,33 @@
|
|
|
508
508
|
color: var(--paragraph-color, #212529);
|
|
509
509
|
}
|
|
510
510
|
|
|
511
|
+
.cellContent {
|
|
512
|
+
display: inline-block;
|
|
513
|
+
width: 100%;
|
|
514
|
+
|
|
515
|
+
/* Ensure date fields have uniform width */
|
|
516
|
+
&[data-type='date'] {
|
|
517
|
+
min-width: 120px;
|
|
518
|
+
width: 120px;
|
|
519
|
+
max-width: 120px;
|
|
520
|
+
display: inline-block;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
&[data-type='datetime'] {
|
|
524
|
+
min-width: 160px;
|
|
525
|
+
width: 160px;
|
|
526
|
+
max-width: 160px;
|
|
527
|
+
display: inline-block;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/* Ensure <br> tags are properly displayed */
|
|
531
|
+
br {
|
|
532
|
+
display: block;
|
|
533
|
+
content: '';
|
|
534
|
+
margin-top: 5px;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
511
538
|
.clickableCell {
|
|
512
539
|
cursor: pointer;
|
|
513
540
|
transition: background-color 0.2s ease;
|
|
@@ -262,6 +262,33 @@
|
|
|
262
262
|
text-align: center;
|
|
263
263
|
word-break: break-word;
|
|
264
264
|
height: 100%;
|
|
265
|
+
position: relative;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Custom tooltip implementation
|
|
269
|
+
.tableTooltip {
|
|
270
|
+
position: fixed;
|
|
271
|
+
background-color: rgba(0, 0, 0, 0.85);
|
|
272
|
+
color: white;
|
|
273
|
+
padding: 10px 14px;
|
|
274
|
+
border-radius: 6px;
|
|
275
|
+
font-size: 0.9em;
|
|
276
|
+
line-height: 1.4;
|
|
277
|
+
white-space: normal;
|
|
278
|
+
width: 300px;
|
|
279
|
+
max-width: 80vw;
|
|
280
|
+
z-index: 9999;
|
|
281
|
+
text-align: left;
|
|
282
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
|
283
|
+
pointer-events: none;
|
|
284
|
+
opacity: 0;
|
|
285
|
+
visibility: hidden;
|
|
286
|
+
transition: opacity 0.2s ease, visibility 0.2s ease;
|
|
287
|
+
|
|
288
|
+
&.visible {
|
|
289
|
+
opacity: 1;
|
|
290
|
+
visibility: visible;
|
|
291
|
+
}
|
|
265
292
|
}
|
|
266
293
|
|
|
267
294
|
.tableItem:hover {
|
|
@@ -502,6 +502,32 @@
|
|
|
502
502
|
padding-right: 10px;
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
+
/* Date field styling for uniform width */
|
|
506
|
+
:global {
|
|
507
|
+
/* Target date cells specifically */
|
|
508
|
+
.InovuaReactDataGrid__cell[data-column-id*='date'],
|
|
509
|
+
.InovuaReactDataGrid__cell[data-column-id*='Date'] {
|
|
510
|
+
min-width: 120px !important;
|
|
511
|
+
width: 120px !important;
|
|
512
|
+
max-width: 120px !important;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/* Target datetime cells specifically */
|
|
516
|
+
.InovuaReactDataGrid__cell[data-column-id*='datetime'],
|
|
517
|
+
.InovuaReactDataGrid__cell[data-column-id*='Datetime'] {
|
|
518
|
+
min-width: 160px !important;
|
|
519
|
+
width: 160px !important;
|
|
520
|
+
max-width: 160px !important;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/* Target daterange cells specifically */
|
|
524
|
+
.InovuaReactDataGrid__cell[data-column-id*='daterange'] {
|
|
525
|
+
min-width: 200px !important;
|
|
526
|
+
width: 200px !important;
|
|
527
|
+
max-width: 200px !important;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
505
531
|
.searchContainer {
|
|
506
532
|
display: flex;
|
|
507
533
|
align-items: center;
|