@visns-studio/visns-components 5.10.10 → 5.10.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
@@ -87,7 +87,7 @@
87
87
  "react-dom": "^17.0.0 || ^18.0.0"
88
88
  },
89
89
  "name": "@visns-studio/visns-components",
90
- "version": "5.10.10",
90
+ "version": "5.10.11",
91
91
  "description": "Various packages to assist in the development of our Custom Applications.",
92
92
  "main": "src/index.js",
93
93
  "files": [
@@ -763,9 +763,89 @@ const DataGrid = ({
763
763
  data[column.id] &&
764
764
  data[column.id][column.jsonData]
765
765
  ) {
766
- data = data[column.id][column.jsonData];
767
-
768
- return <span>{data}</span>;
766
+ const jsonValue = data[column.id][column.jsonData];
767
+
768
+ if (!jsonValue) return null;
769
+
770
+ try {
771
+ const jsonData = typeof jsonValue === 'string' ? JSON.parse(jsonValue) : jsonValue;
772
+
773
+ // Check if this looks like a question/answer format
774
+ const isQuestionAnswerFormat = Object.values(jsonData).every(item =>
775
+ item && typeof item === 'object' &&
776
+ (item.hasOwnProperty('question') || item.hasOwnProperty('answer'))
777
+ );
778
+
779
+ if (isQuestionAnswerFormat) {
780
+ return (
781
+ <div style={{ fontSize: '0.8rem' }}>
782
+ {Object.entries(jsonData).map(([key, item]) => {
783
+ if (!item || typeof item !== 'object') return null;
784
+
785
+ const question = item.question || key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
786
+ const answer = item.answer !== undefined ?
787
+ (typeof item.answer === 'boolean' ? (item.answer ? 'Yes' : 'No') : String(item.answer)) :
788
+ 'N/A';
789
+
790
+ return (
791
+ <div key={key} style={{ marginBottom: '0.3rem' }}>
792
+ <div style={{ fontWeight: '600', color: 'var(--primary-color)', marginBottom: '0.1rem', fontSize: '0.75rem' }}>
793
+ {question.length > 60 ? question.substring(0, 60) + '...' : question}
794
+ </div>
795
+ <div style={{ marginLeft: '0.5rem', color: 'var(--paragraph-color)', fontSize: '0.75rem' }}>
796
+ {answer}
797
+ </div>
798
+ </div>
799
+ );
800
+ })}
801
+ </div>
802
+ );
803
+ }
804
+
805
+ // Check if this is a simple key-value object
806
+ const isSimpleKeyValue = Object.values(jsonData).every(val =>
807
+ typeof val !== 'object' || val === null
808
+ );
809
+
810
+ if (isSimpleKeyValue) {
811
+ return (
812
+ <div style={{ fontSize: '0.8rem' }}>
813
+ {Object.entries(jsonData).slice(0, 3).map(([key, val]) => (
814
+ <div key={key} style={{ marginBottom: '0.2rem', display: 'flex', gap: '0.3rem' }}>
815
+ <strong style={{ color: 'var(--primary-color)', fontSize: '0.7rem' }}>
816
+ {key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}:
817
+ </strong>
818
+ <span style={{ color: 'var(--paragraph-color)', fontSize: '0.7rem' }}>
819
+ {val !== null && val !== undefined ?
820
+ (typeof val === 'boolean' ? (val ? 'Yes' : 'No') : String(val).substring(0, 20)) :
821
+ 'N/A'}
822
+ </span>
823
+ </div>
824
+ ))}
825
+ {Object.keys(jsonData).length > 3 && (
826
+ <div style={{ fontSize: '0.65rem', color: 'var(--secondary-color)', fontStyle: 'italic' }}>
827
+ +{Object.keys(jsonData).length - 3} more...
828
+ </div>
829
+ )}
830
+ </div>
831
+ );
832
+ }
833
+
834
+ // For complex nested objects, show a summary
835
+ const keyCount = Object.keys(jsonData).length;
836
+ return (
837
+ <div style={{ fontSize: '0.75rem', color: 'var(--paragraph-color)', fontStyle: 'italic' }}>
838
+ JSON ({keyCount} {keyCount === 1 ? 'property' : 'properties'})
839
+ </div>
840
+ );
841
+
842
+ } catch (error) {
843
+ // If parsing fails, show raw value truncated
844
+ const displayValue = typeof jsonValue === 'string' && jsonValue.length > 30
845
+ ? jsonValue.substring(0, 30) + '...'
846
+ : jsonValue;
847
+ return <span style={{ fontSize: '0.75rem', fontFamily: 'monospace', color: 'var(--paragraph-color)' }}>{displayValue}</span>;
848
+ }
769
849
  } else {
770
850
  return null;
771
851
  }
@@ -376,13 +376,108 @@ export const renderJsonColumn = ({ column, commonProps }) => {
376
376
  data[column.id][column.jsonData]
377
377
  ) {
378
378
  const jsonValue = data[column.id][column.jsonData];
379
+
380
+ if (!jsonValue) return null;
381
+
382
+ let displayContent;
383
+ let tooltipValue = jsonValue;
384
+
385
+ try {
386
+ const jsonData = typeof jsonValue === 'string' ? JSON.parse(jsonValue) : jsonValue;
387
+
388
+ // Check if this looks like a question/answer format
389
+ const isQuestionAnswerFormat = Object.values(jsonData).every(item =>
390
+ item && typeof item === 'object' &&
391
+ (item.hasOwnProperty('question') || item.hasOwnProperty('answer'))
392
+ );
393
+
394
+ if (isQuestionAnswerFormat) {
395
+ const entries = Object.entries(jsonData);
396
+ if (entries.length === 1) {
397
+ const [key, item] = entries[0];
398
+ const question = item.question || key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
399
+ const answer = item.answer !== undefined ?
400
+ (typeof item.answer === 'boolean' ? (item.answer ? 'Yes' : 'No') : String(item.answer)) :
401
+ 'N/A';
402
+
403
+ displayContent = (
404
+ <div style={{ fontSize: '0.8rem' }}>
405
+ <div style={{ fontWeight: '600', color: 'var(--primary-color)', marginBottom: '0.1rem', fontSize: '0.75rem' }}>
406
+ {question.length > 50 ? question.substring(0, 50) + '...' : question}
407
+ </div>
408
+ <div style={{ color: 'var(--paragraph-color)', fontSize: '0.75rem' }}>
409
+ {answer}
410
+ </div>
411
+ </div>
412
+ );
413
+ } else {
414
+ displayContent = (
415
+ <div style={{ fontSize: '0.75rem', color: 'var(--paragraph-color)' }}>
416
+ Q&A ({entries.length} items)
417
+ </div>
418
+ );
419
+ }
420
+ tooltipValue = JSON.stringify(jsonData, null, 2);
421
+ }
422
+ // Check if this is a simple key-value object
423
+ else if (Object.values(jsonData).every(val => typeof val !== 'object' || val === null)) {
424
+ const entries = Object.entries(jsonData);
425
+ if (entries.length <= 2) {
426
+ displayContent = (
427
+ <div style={{ fontSize: '0.8rem' }}>
428
+ {entries.map(([key, val]) => (
429
+ <div key={key} style={{ marginBottom: '0.1rem', display: 'flex', gap: '0.3rem' }}>
430
+ <strong style={{ color: 'var(--primary-color)', fontSize: '0.7rem' }}>
431
+ {key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}:
432
+ </strong>
433
+ <span style={{ color: 'var(--paragraph-color)', fontSize: '0.7rem' }}>
434
+ {val !== null && val !== undefined ?
435
+ (typeof val === 'boolean' ? (val ? 'Yes' : 'No') : String(val).substring(0, 15)) :
436
+ 'N/A'}
437
+ </span>
438
+ </div>
439
+ ))}
440
+ </div>
441
+ );
442
+ } else {
443
+ displayContent = (
444
+ <div style={{ fontSize: '0.75rem', color: 'var(--paragraph-color)' }}>
445
+ Data ({entries.length} properties)
446
+ </div>
447
+ );
448
+ }
449
+ tooltipValue = JSON.stringify(jsonData, null, 2);
450
+ }
451
+ // For complex nested objects
452
+ else {
453
+ const keyCount = Object.keys(jsonData).length;
454
+ displayContent = (
455
+ <div style={{ fontSize: '0.75rem', color: 'var(--paragraph-color)', fontStyle: 'italic' }}>
456
+ JSON ({keyCount} {keyCount === 1 ? 'property' : 'properties'})
457
+ </div>
458
+ );
459
+ tooltipValue = JSON.stringify(jsonData, null, 2);
460
+ }
461
+
462
+ } catch (error) {
463
+ // If parsing fails, show truncated raw value
464
+ const displayValue = typeof jsonValue === 'string' && jsonValue.length > 30
465
+ ? jsonValue.substring(0, 30) + '...'
466
+ : jsonValue;
467
+ displayContent = (
468
+ <span style={{ fontSize: '0.75rem', fontFamily: 'monospace', color: 'var(--paragraph-color)' }}>
469
+ {displayValue}
470
+ </span>
471
+ );
472
+ tooltipValue = jsonValue;
473
+ }
379
474
 
380
475
  return (
381
476
  <CellWithTooltip
382
- value={jsonValue}
477
+ value={tooltipValue}
383
478
  columnType="json"
384
479
  >
385
- <span>{jsonValue}</span>
480
+ {displayContent}
386
481
  </CellWithTooltip>
387
482
  );
388
483
  } else {
@@ -686,8 +686,73 @@ function GenericDetail({
686
686
 
687
687
  const renderExist = () => (truncatedValue ? 'Yes' : 'No');
688
688
 
689
- const renderJson = () =>
690
- value ? <pre>{JSON.stringify(value, null, 4)}</pre> : null;
689
+ const renderJson = () => {
690
+ if (!value) return null;
691
+
692
+ try {
693
+ const jsonData = typeof value === 'string' ? JSON.parse(value) : value;
694
+
695
+ // Check if this looks like a question/answer format
696
+ const isQuestionAnswerFormat = Object.values(jsonData).every(item =>
697
+ item && typeof item === 'object' &&
698
+ (item.hasOwnProperty('question') || item.hasOwnProperty('answer'))
699
+ );
700
+
701
+ if (isQuestionAnswerFormat) {
702
+ return (
703
+ <div className={styles.jsonQuestionAnswer}>
704
+ {Object.entries(jsonData).map(([key, item]) => {
705
+ if (!item || typeof item !== 'object') return null;
706
+
707
+ const question = item.question || key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
708
+ const answer = item.answer !== undefined ?
709
+ (typeof item.answer === 'boolean' ? (item.answer ? 'Yes' : 'No') : String(item.answer)) :
710
+ 'N/A';
711
+
712
+ return (
713
+ <div key={key} className={styles.questionAnswerPair}>
714
+ <div className={styles.question}>
715
+ <strong>{question}</strong>
716
+ </div>
717
+ <div className={styles.answer}>
718
+ {answer}
719
+ </div>
720
+ </div>
721
+ );
722
+ })}
723
+ </div>
724
+ );
725
+ }
726
+
727
+ // Check if this is a simple key-value object
728
+ const isSimpleKeyValue = Object.values(jsonData).every(val =>
729
+ typeof val !== 'object' || val === null
730
+ );
731
+
732
+ if (isSimpleKeyValue) {
733
+ return (
734
+ <div className={styles.jsonKeyValue}>
735
+ {Object.entries(jsonData).map(([key, val]) => (
736
+ <div key={key} className={styles.keyValuePair}>
737
+ <strong>{key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}:</strong> {
738
+ val !== null && val !== undefined ?
739
+ (typeof val === 'boolean' ? (val ? 'Yes' : 'No') : String(val)) :
740
+ 'N/A'
741
+ }
742
+ </div>
743
+ ))}
744
+ </div>
745
+ );
746
+ }
747
+
748
+ // For complex nested objects, fall back to formatted JSON
749
+ return <pre className={styles.jsonRaw}>{JSON.stringify(jsonData, null, 2)}</pre>;
750
+
751
+ } catch (error) {
752
+ // If parsing fails, show raw value
753
+ return <pre className={styles.jsonRaw}>{JSON.stringify(value, null, 2)}</pre>;
754
+ }
755
+ };
691
756
 
692
757
  const renderLatestNotes = () => formatNoteValue();
693
758
 
@@ -742,3 +742,80 @@
742
742
  box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1); /* Gentle shadow for depth */
743
743
  font-style: italic; /* Optional: Add a subtle font style */
744
744
  }
745
+
746
+ /* Enhanced JSON Rendering Styles */
747
+ .jsonQuestionAnswer {
748
+ padding: 0.75rem;
749
+ background: rgba(var(--primary-rgb), 0.02);
750
+ border-radius: 4px;
751
+ border: 1px solid rgba(var(--primary-rgb), 0.08);
752
+ }
753
+
754
+ .questionAnswerPair {
755
+ margin-bottom: 1rem;
756
+ padding: 0.75rem;
757
+ background: rgba(var(--primary-rgb), 0.01);
758
+ border-radius: 4px;
759
+ border-left: 3px solid var(--secondary-color);
760
+
761
+ &:last-child {
762
+ margin-bottom: 0;
763
+ }
764
+ }
765
+
766
+ .question {
767
+ margin-bottom: 0.4rem;
768
+
769
+ strong {
770
+ color: var(--primary-color);
771
+ font-size: 0.9rem;
772
+ font-weight: 600;
773
+ }
774
+ }
775
+
776
+ .answer {
777
+ color: var(--paragraph-color);
778
+ font-size: 0.85rem;
779
+ margin-left: 1rem;
780
+ font-weight: 500;
781
+ }
782
+
783
+ .jsonKeyValue {
784
+ padding: 0.75rem;
785
+ background: rgba(var(--primary-rgb), 0.02);
786
+ border-radius: 4px;
787
+ border: 1px solid rgba(var(--primary-rgb), 0.08);
788
+ }
789
+
790
+ .keyValuePair {
791
+ margin-bottom: 0.6rem;
792
+ padding: 0.5rem;
793
+ background: rgba(var(--primary-rgb), 0.01);
794
+ border-radius: 3px;
795
+ display: flex;
796
+ gap: 0.5rem;
797
+
798
+ &:last-child {
799
+ margin-bottom: 0;
800
+ }
801
+
802
+ strong {
803
+ color: var(--primary-color);
804
+ font-size: 0.85rem;
805
+ min-width: 120px;
806
+ flex: 0 0 auto;
807
+ }
808
+ }
809
+
810
+ .jsonRaw {
811
+ background: rgba(var(--primary-rgb), 0.03);
812
+ border: 1px solid rgba(var(--primary-rgb), 0.1);
813
+ border-radius: 4px;
814
+ padding: 0.75rem;
815
+ font-family: monospace;
816
+ font-size: 0.8rem;
817
+ line-height: 1.4;
818
+ color: var(--paragraph-color);
819
+ overflow-x: auto;
820
+ white-space: pre-wrap;
821
+ }