@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 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.9",
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
- formatCellContent(
1222
- row[
1223
- header.key
1224
- ],
1225
- {
1226
- type:
1227
- header.type ||
1228
- (typeof row[
1229
- header
1230
- .key
1231
- ] ===
1232
- 'number'
1233
- ? 'number'
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
- {formatCellContent(
1260
- row[col.name],
1261
- col
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
- // Filter out the hidden tables and pivot tables (tables with underscore in name)
947
- const filteredTables = tablesData.filter(
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
- <div
3466
- key={table.name}
3467
- className={`${styles.tableItem} ${
3468
- selectedTable === table.name
3469
- ? styles.tableItemSelected
3470
- : ''
3471
- }`}
3472
- onClick={() =>
3473
- handleTableSelect(table.name)
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
- {formatName(table.name)}
3477
- </div>
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;