@visns-studio/visns-components 5.12.15 → 5.13.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.
@@ -144,16 +144,19 @@ const getAcceptTypes = (filetype) => {
144
144
  return {
145
145
  'application/pdf': ['.pdf'],
146
146
  'application/msword': ['.doc'],
147
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'],
147
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
148
+ ['.docx'],
148
149
  'application/vnd.ms-excel': ['.xls'],
149
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'],
150
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
151
+ ['.xlsx'],
150
152
  'application/vnd.ms-powerpoint': ['.ppt'],
151
- 'application/vnd.openxmlformats-officedocument.presentationml.presentation': ['.pptx'],
153
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
154
+ ['.pptx'],
152
155
  'text/plain': ['.txt'],
153
156
  'text/csv': ['.csv'],
154
157
  'application/zip': ['.zip'],
155
158
  'application/x-rar-compressed': ['.rar'],
156
- 'application/x-7z-compressed': ['.7z']
159
+ 'application/x-7z-compressed': ['.7z'],
157
160
  };
158
161
  case 'image':
159
162
  return {
@@ -163,7 +166,7 @@ const getAcceptTypes = (filetype) => {
163
166
  'image/webp': ['.webp'],
164
167
  'image/svg+xml': ['.svg'],
165
168
  'image/bmp': ['.bmp'],
166
- 'image/tiff': ['.tiff', '.tif']
169
+ 'image/tiff': ['.tiff', '.tif'],
167
170
  };
168
171
  default:
169
172
  return undefined; // Accept all file types
@@ -173,7 +176,7 @@ const getAcceptTypes = (filetype) => {
173
176
  // Helper function to get the appropriate icon based on file extension
174
177
  const getFileIcon = (filename) => {
175
178
  const extension = filename.split('.').pop().toLowerCase();
176
-
179
+
177
180
  switch (extension) {
178
181
  case 'pdf':
179
182
  return <FileText strokeWidth={2} size={20} color="#dc2626" />;
@@ -182,7 +185,9 @@ const getFileIcon = (filename) => {
182
185
  return <FileText strokeWidth={2} size={20} color="#2563eb" />;
183
186
  case 'xls':
184
187
  case 'xlsx':
185
- return <FileSpreadsheet strokeWidth={2} size={20} color="#16a34a" />;
188
+ return (
189
+ <FileSpreadsheet strokeWidth={2} size={20} color="#16a34a" />
190
+ );
186
191
  case 'ppt':
187
192
  case 'pptx':
188
193
  return <FileText strokeWidth={2} size={20} color="#ea580c" />;
@@ -211,11 +216,11 @@ const getFileIcon = (filename) => {
211
216
  // Helper function to format file size
212
217
  const formatFileSize = (bytes) => {
213
218
  if (!bytes || bytes === 0) return '';
214
-
219
+
215
220
  const k = 1024;
216
221
  const sizes = ['B', 'KB', 'MB', 'GB'];
217
222
  const i = Math.floor(Math.log(bytes) / Math.log(k));
218
-
223
+
219
224
  return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
220
225
  };
221
226
 
@@ -425,14 +430,14 @@ function GenericDetail({
425
430
  const handleStagePopupConfirm = async (popupFormData) => {
426
431
  try {
427
432
  setShowStagePopup(false);
428
-
433
+
429
434
  if (!stagePopupData) return;
430
-
435
+
431
436
  const { stage, type } = stagePopupData;
432
-
437
+
433
438
  // Proceed with normal stage update, but include popup form data
434
439
  await performStageUpdate(stage, type, popupFormData);
435
-
440
+
436
441
  setStagePopupData(null);
437
442
  } catch (error) {
438
443
  console.error('Stage popup confirm error:', error);
@@ -456,56 +461,76 @@ function GenericDetail({
456
461
  setActiveStage(stage.id);
457
462
  } else if (type === 'stageCounter') {
458
463
  // Handle new stageCounter functionality
459
- console.log('performStageUpdate called with stageCounter type:', { stage, data, stages });
460
-
461
- const stageConfig = stages.stageConfig || activeTabConfig.stages?.stageConfig;
462
- if (!stageConfig?.toggleable || !stageConfig?.toggleConfig) {
463
- console.warn('Stage is not configured for toggling', { stageConfig });
464
+ console.log(
465
+ 'performStageUpdate called with stageCounter type:',
466
+ { stage, data, stages }
467
+ );
468
+
469
+ const stageConfig =
470
+ stages.stageConfig ||
471
+ activeTabConfig.stages?.stageConfig;
472
+ if (
473
+ !stageConfig?.toggleable ||
474
+ !stageConfig?.toggleConfig
475
+ ) {
476
+ console.warn('Stage is not configured for toggling', {
477
+ stageConfig,
478
+ });
464
479
  return;
465
480
  }
466
481
 
467
482
  // Get current stage data
468
483
  const stageDataArray = data[stageConfig.key] || [];
469
- const currentStage = stageDataArray.find(s => s.id === stage.id);
470
-
484
+ const currentStage = stageDataArray.find(
485
+ (s) => s.id === stage.id
486
+ );
487
+
471
488
  if (!currentStage) {
472
489
  console.warn('Stage not found in data');
473
490
  return;
474
491
  }
475
492
 
476
493
  // Determine current and target status
477
- const currentStatus = currentStage[stageConfig.toggleConfig.statusField];
478
- const isActive = currentStatus === stageConfig.toggleConfig.activeStatusValue;
479
- const targetStatus = isActive
480
- ? stageConfig.toggleConfig.inactiveStatusValue
494
+ const currentStatus =
495
+ currentStage[stageConfig.toggleConfig.statusField];
496
+ const isActive =
497
+ currentStatus ===
498
+ stageConfig.toggleConfig.activeStatusValue;
499
+ const targetStatus = isActive
500
+ ? stageConfig.toggleConfig.inactiveStatusValue
481
501
  : stageConfig.toggleConfig.activeStatusValue;
482
502
 
483
503
  // Build API URL
484
504
  let url = stageConfig.toggleConfig.updateUrl;
485
505
  url = url.replace('{id}', routeParams[stages.urlParam]);
486
506
  url = url.replace('{stage_id}', stage.id);
487
-
507
+
488
508
  // Make API call with additional data from popup
489
509
  const res = await CustomFetch(
490
510
  url,
491
511
  stageConfig.toggleConfig.updateMethod || 'PUT',
492
512
  {
493
513
  [stageConfig.toggleConfig.updateKey]: targetStatus,
494
- ...additionalData
514
+ ...additionalData,
495
515
  }
496
516
  );
497
517
 
498
518
  if (res.data.error === '') {
499
519
  handleReload();
500
520
  const actionText = isActive ? 'disabled' : 'enabled';
501
- toast.success(`Stage "${stage.label}" ${actionText} successfully`);
521
+ toast.success(
522
+ `Stage "${stage.label}" ${actionText} successfully`
523
+ );
502
524
  } else {
503
525
  toast.error(res.data.error);
504
526
  }
505
527
  } else if (type === 'legacy_toggle') {
506
528
  // Handle legacy toggle functionality - toggle between current stage and previous
507
- console.log('performStageUpdate called with legacy_toggle type:', { stage, data, stages });
508
-
529
+ console.log(
530
+ 'performStageUpdate called with legacy_toggle type:',
531
+ { stage, data, stages }
532
+ );
533
+
509
534
  if (
510
535
  stages &&
511
536
  stages.url &&
@@ -517,7 +542,7 @@ function GenericDetail({
517
542
  ) {
518
543
  const currentStatus = data[stages.key];
519
544
  let targetStatus;
520
-
545
+
521
546
  // If clicking on the current stage (completed), move back one step
522
547
  // If clicking on a future stage, move to that stage
523
548
  if (currentStatus >= stage.id) {
@@ -528,7 +553,9 @@ function GenericDetail({
528
553
  targetStatus = stage.id;
529
554
  }
530
555
 
531
- const url = `${stages.url}/${routeParams[stages.urlParam]}`;
556
+ const url = `${stages.url}/${
557
+ routeParams[stages.urlParam]
558
+ }`;
532
559
 
533
560
  // Include additional data from popup
534
561
  const res = await CustomFetch(
@@ -536,14 +563,19 @@ function GenericDetail({
536
563
  stages.method || 'PUT',
537
564
  {
538
565
  [stages.key]: targetStatus,
539
- ...additionalData
566
+ ...additionalData,
540
567
  }
541
568
  );
542
569
 
543
570
  if (res.data.error === '') {
544
571
  handleReload();
545
- const actionText = currentStatus >= stage.id ? 'moved back from' : 'progressed to';
546
- toast.success(`Stage ${actionText} "${stage.label}" successfully`);
572
+ const actionText =
573
+ currentStatus >= stage.id
574
+ ? 'moved back from'
575
+ : 'progressed to';
576
+ toast.success(
577
+ `Stage ${actionText} "${stage.label}" successfully`
578
+ );
547
579
  } else {
548
580
  toast.error(res.data.error);
549
581
  }
@@ -563,7 +595,7 @@ function GenericDetail({
563
595
 
564
596
  const res = await CustomFetch(url, 'POST', {
565
597
  [activeTabConfig.stages.key]: stage.id,
566
- ...additionalData
598
+ ...additionalData,
567
599
  });
568
600
 
569
601
  if (res.data.error === '') {
@@ -588,7 +620,7 @@ function GenericDetail({
588
620
  stages.method || 'POST',
589
621
  {
590
622
  [stages.key]: stage.id,
591
- ...additionalData
623
+ ...additionalData,
592
624
  }
593
625
  );
594
626
 
@@ -940,29 +972,48 @@ function GenericDetail({
940
972
 
941
973
  const renderJson = () => {
942
974
  if (!value) return null;
943
-
975
+
944
976
  try {
945
- const jsonData = typeof value === 'string' ? JSON.parse(value) : value;
946
-
977
+ const jsonData =
978
+ typeof value === 'string' ? JSON.parse(value) : value;
979
+
947
980
  // Check if this looks like a question/answer format
948
- const isQuestionAnswerFormat = Object.values(jsonData).every(item =>
949
- item && typeof item === 'object' &&
950
- (item.hasOwnProperty('question') || item.hasOwnProperty('answer'))
981
+ const isQuestionAnswerFormat = Object.values(jsonData).every(
982
+ (item) =>
983
+ item &&
984
+ typeof item === 'object' &&
985
+ (item.hasOwnProperty('question') ||
986
+ item.hasOwnProperty('answer'))
951
987
  );
952
-
988
+
953
989
  if (isQuestionAnswerFormat) {
954
990
  return (
955
991
  <div className={styles.jsonQuestionAnswer}>
956
992
  {Object.entries(jsonData).map(([key, item]) => {
957
- if (!item || typeof item !== 'object') return null;
958
-
959
- const question = item.question || key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
960
- const answer = item.answer !== undefined ?
961
- (typeof item.answer === 'boolean' ? (item.answer ? 'Yes' : 'No') : String(item.answer)) :
962
- 'N/A';
963
-
993
+ if (!item || typeof item !== 'object')
994
+ return null;
995
+
996
+ const question =
997
+ item.question ||
998
+ key
999
+ .replace(/_/g, ' ')
1000
+ .replace(/\b\w/g, (l) =>
1001
+ l.toUpperCase()
1002
+ );
1003
+ const answer =
1004
+ item.answer !== undefined
1005
+ ? typeof item.answer === 'boolean'
1006
+ ? item.answer
1007
+ ? 'Yes'
1008
+ : 'No'
1009
+ : String(item.answer)
1010
+ : 'N/A';
1011
+
964
1012
  return (
965
- <div key={key} className={styles.questionAnswerPair}>
1013
+ <div
1014
+ key={key}
1015
+ className={styles.questionAnswerPair}
1016
+ >
966
1017
  <div className={styles.question}>
967
1018
  <strong>{question}</strong>
968
1019
  </div>
@@ -975,34 +1026,51 @@ function GenericDetail({
975
1026
  </div>
976
1027
  );
977
1028
  }
978
-
1029
+
979
1030
  // Check if this is a simple key-value object
980
- const isSimpleKeyValue = Object.values(jsonData).every(val =>
981
- typeof val !== 'object' || val === null
1031
+ const isSimpleKeyValue = Object.values(jsonData).every(
1032
+ (val) => typeof val !== 'object' || val === null
982
1033
  );
983
-
1034
+
984
1035
  if (isSimpleKeyValue) {
985
1036
  return (
986
1037
  <div className={styles.jsonKeyValue}>
987
1038
  {Object.entries(jsonData).map(([key, val]) => (
988
1039
  <div key={key} className={styles.keyValuePair}>
989
- <strong>{key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}:</strong> {
990
- val !== null && val !== undefined ?
991
- (typeof val === 'boolean' ? (val ? 'Yes' : 'No') : String(val)) :
992
- 'N/A'
993
- }
1040
+ <strong>
1041
+ {key
1042
+ .replace(/_/g, ' ')
1043
+ .replace(/\b\w/g, (l) =>
1044
+ l.toUpperCase()
1045
+ )}
1046
+ :
1047
+ </strong>{' '}
1048
+ {val !== null && val !== undefined
1049
+ ? typeof val === 'boolean'
1050
+ ? val
1051
+ ? 'Yes'
1052
+ : 'No'
1053
+ : String(val)
1054
+ : 'N/A'}
994
1055
  </div>
995
1056
  ))}
996
1057
  </div>
997
1058
  );
998
1059
  }
999
-
1060
+
1000
1061
  // For complex nested objects, fall back to formatted JSON
1001
- return <pre className={styles.jsonRaw}>{JSON.stringify(jsonData, null, 2)}</pre>;
1002
-
1062
+ return (
1063
+ <pre className={styles.jsonRaw}>
1064
+ {JSON.stringify(jsonData, null, 2)}
1065
+ </pre>
1066
+ );
1003
1067
  } catch (error) {
1004
1068
  // If parsing fails, show raw value
1005
- return <pre className={styles.jsonRaw}>{JSON.stringify(value, null, 2)}</pre>;
1069
+ return (
1070
+ <pre className={styles.jsonRaw}>
1071
+ {JSON.stringify(value, null, 2)}
1072
+ </pre>
1073
+ );
1006
1074
  }
1007
1075
  };
1008
1076
 
@@ -1608,7 +1676,9 @@ function GenericDetail({
1608
1676
  />
1609
1677
  </div>
1610
1678
  <Dropzone
1611
- accept={getAcceptTypes(activeTabConfig.filetype)}
1679
+ accept={getAcceptTypes(
1680
+ activeTabConfig.filetype
1681
+ )}
1612
1682
  onDrop={(acceptedFiles) => {
1613
1683
  const uploadFile = async (file) => {
1614
1684
  // Optimize image if it's an image file
@@ -1804,7 +1874,9 @@ function GenericDetail({
1804
1874
  }
1805
1875
  >
1806
1876
  {files &&
1807
- Array.isArray(files) &&
1877
+ Array.isArray(
1878
+ files
1879
+ ) &&
1808
1880
  files.map(
1809
1881
  (a, b) => {
1810
1882
  const imageSource =
@@ -1829,33 +1901,59 @@ function GenericDetail({
1829
1901
  alt={
1830
1902
  a.file_name
1831
1903
  }
1832
- onClick={(e) => {
1904
+ onClick={(
1905
+ e
1906
+ ) => {
1833
1907
  e.stopPropagation();
1834
1908
  e.preventDefault();
1835
-
1909
+
1836
1910
  // Prepare gallery data in the format expected by Lightbox
1837
- const galleryArray = files
1838
- .filter(file => {
1839
- const imageSource = file.base64_encoded_image || file.s3_url || file.file_url;
1840
- return imageSource;
1841
- })
1842
- .map((file) => ({
1843
- src: file.base64_encoded_image || file.s3_url || file.file_url,
1844
- caption: file.file_name,
1845
- width: 320,
1846
- height: 240,
1847
- }));
1911
+ const galleryArray =
1912
+ files
1913
+ .filter(
1914
+ (
1915
+ file
1916
+ ) => {
1917
+ const imageSource =
1918
+ file.base64_encoded_image ||
1919
+ file.s3_url ||
1920
+ file.file_url;
1921
+ return imageSource;
1922
+ }
1923
+ )
1924
+ .map(
1925
+ (
1926
+ file
1927
+ ) => ({
1928
+ src:
1929
+ file.base64_encoded_image ||
1930
+ file.s3_url ||
1931
+ file.file_url,
1932
+ caption:
1933
+ file.file_name,
1934
+ width: 320,
1935
+ height: 240,
1936
+ })
1937
+ );
1848
1938
 
1849
1939
  // Set gallery data for lightbox
1850
- setGalleryData(galleryArray);
1851
-
1940
+ setGalleryData(
1941
+ galleryArray
1942
+ );
1943
+
1852
1944
  // Set initial lightbox index
1853
- setLightboxIndex(b);
1854
-
1945
+ setLightboxIndex(
1946
+ b
1947
+ );
1948
+
1855
1949
  // Open lightbox directly
1856
- setLightboxOpen(true);
1950
+ setLightboxOpen(
1951
+ true
1952
+ );
1953
+ }}
1954
+ style={{
1955
+ cursor: 'pointer',
1857
1956
  }}
1858
- style={{ cursor: 'pointer' }}
1859
1957
  />
1860
1958
  <TrashCan
1861
1959
  onClick={(
@@ -1912,14 +2010,20 @@ function GenericDetail({
1912
2010
  );
1913
2011
  }}
1914
2012
  >
1915
- {getFileIcon(a.file_name)}
2013
+ {getFileIcon(
2014
+ a.file_name
2015
+ )}
1916
2016
  <div className="filename">
1917
2017
  <div className="file-name">
1918
- {a.file_name}
2018
+ {
2019
+ a.file_name
2020
+ }
1919
2021
  </div>
1920
2022
  {a.file_size && (
1921
2023
  <div className="file-size">
1922
- {formatFileSize(a.file_size)}
2024
+ {formatFileSize(
2025
+ a.file_size
2026
+ )}
1923
2027
  </div>
1924
2028
  )}
1925
2029
  </div>
@@ -2474,7 +2578,8 @@ function GenericDetail({
2474
2578
  processed[key] =
2475
2579
  routeParams[urlParam];
2476
2580
  } else if (
2477
- typeof value === 'object' && value !== null
2581
+ typeof value === 'object' &&
2582
+ value !== null
2478
2583
  ) {
2479
2584
  processed[key] =
2480
2585
  processUrlParams(value);
@@ -2512,7 +2617,9 @@ function GenericDetail({
2512
2617
  }
2513
2618
  >
2514
2619
  <Dropzone
2515
- accept={getAcceptTypes(activeTabConfig.filetype)}
2620
+ accept={getAcceptTypes(
2621
+ activeTabConfig.filetype
2622
+ )}
2516
2623
  onDrop={(
2517
2624
  acceptedFiles
2518
2625
  ) => {
@@ -2753,21 +2860,34 @@ function GenericDetail({
2753
2860
  // Replace URL parameters like {dataId} with actual values
2754
2861
  const replaceUrlParams = (url) => {
2755
2862
  if (!url) return url;
2756
- return url.replace(/{dataId}/g, routeParams[urlParam] || '');
2863
+ return url.replace(
2864
+ /{dataId}/g,
2865
+ routeParams[urlParam] || ''
2866
+ );
2757
2867
  };
2758
-
2868
+
2759
2869
  return (
2760
2870
  <CategorizedDropZone
2761
2871
  filetype={activeTabConfig.filetype}
2762
2872
  folder={activeTabConfig.folder}
2763
2873
  dataKey={activeTabConfig.key}
2764
- file_relationship={activeTabConfig.file_relationship}
2874
+ file_relationship={
2875
+ activeTabConfig.file_relationship
2876
+ }
2765
2877
  url={replaceUrlParams(activeTabConfig.url)}
2766
2878
  method={activeTabConfig.method}
2767
- deleteUrl={replaceUrlParams(activeTabConfig.deleteUrl)}
2768
- categories={activeTabConfig.categories || []}
2769
- categoriesUrl={activeTabConfig.categoriesUrl}
2770
- categoriesWhere={activeTabConfig.categoriesWhere}
2879
+ deleteUrl={replaceUrlParams(
2880
+ activeTabConfig.deleteUrl
2881
+ )}
2882
+ categories={
2883
+ activeTabConfig.categories || []
2884
+ }
2885
+ categoriesUrl={
2886
+ activeTabConfig.categoriesUrl
2887
+ }
2888
+ categoriesWhere={
2889
+ activeTabConfig.categoriesWhere
2890
+ }
2771
2891
  entityData={data}
2772
2892
  routeParams={routeParams}
2773
2893
  fetchData={handleReload}
@@ -2786,7 +2906,12 @@ function GenericDetail({
2786
2906
  );
2787
2907
  case 'custom':
2788
2908
  // Handle custom components
2789
- if (activeTabConfig.component === 'AssociationManager' || activeTabConfig.component === 'ClientAssociationManager') {
2909
+ if (
2910
+ activeTabConfig.component ===
2911
+ 'AssociationManager' ||
2912
+ activeTabConfig.component ===
2913
+ 'ClientAssociationManager'
2914
+ ) {
2790
2915
  return (
2791
2916
  <AssociationManager
2792
2917
  endpoints={activeTabConfig.endpoints}
@@ -2806,7 +2931,10 @@ function GenericDetail({
2806
2931
  />
2807
2932
  );
2808
2933
  }
2809
- if (activeTabConfig.component === 'ProposalTemplateSectionManager') {
2934
+ if (
2935
+ activeTabConfig.component ===
2936
+ 'ProposalTemplateSectionManager'
2937
+ ) {
2810
2938
  return (
2811
2939
  <ProposalTemplateSectionManager
2812
2940
  {...activeTabConfig.props}
@@ -2815,7 +2943,10 @@ function GenericDetail({
2815
2943
  />
2816
2944
  );
2817
2945
  }
2818
- if (activeTabConfig.component === 'ProposalTemplatePreview') {
2946
+ if (
2947
+ activeTabConfig.component ===
2948
+ 'ProposalTemplatePreview'
2949
+ ) {
2819
2950
  return (
2820
2951
  <ProposalTemplatePreview
2821
2952
  {...activeTabConfig.props}
@@ -2868,7 +2999,10 @@ function GenericDetail({
2868
2999
  // Update files when active tab changes for dropzone tabs
2869
3000
  useEffect(() => {
2870
3001
  if (activeTabConfig && activeTabConfig.type === 'dropzone' && data) {
2871
- if (activeTabConfig.file_relationship && data[activeTabConfig.file_relationship]) {
3002
+ if (
3003
+ activeTabConfig.file_relationship &&
3004
+ data[activeTabConfig.file_relationship]
3005
+ ) {
2872
3006
  setFiles(data[activeTabConfig.file_relationship]);
2873
3007
  } else if (data.files) {
2874
3008
  setFiles(data.files);
@@ -3178,43 +3312,86 @@ function GenericDetail({
3178
3312
  <ul className={styles.opppath}>
3179
3313
  {stages.data.map((stage) => {
3180
3314
  let isStageComplete = false;
3181
-
3315
+
3182
3316
  if (stages.type === 'stageCounter') {
3183
3317
  // For stageCounter, check individual stage status
3184
- const stageConfig = stages.stageConfig;
3185
- if (stageConfig && data[stageConfig.key]) {
3186
- const stageDataArray = data[stageConfig.key];
3187
- const currentStage = stageDataArray.find(s => s.id === stage.id);
3188
- if (currentStage && stageConfig.toggleConfig) {
3189
- const currentStatus = currentStage[stageConfig.toggleConfig.statusField];
3190
- isStageComplete = currentStatus === stageConfig.toggleConfig.activeStatusValue;
3191
-
3318
+ const stageConfig =
3319
+ stages.stageConfig;
3320
+ if (
3321
+ stageConfig &&
3322
+ data[stageConfig.key]
3323
+ ) {
3324
+ const stageDataArray =
3325
+ data[stageConfig.key];
3326
+ const currentStage =
3327
+ stageDataArray.find(
3328
+ (s) => s.id === stage.id
3329
+ );
3330
+ if (
3331
+ currentStage &&
3332
+ stageConfig.toggleConfig
3333
+ ) {
3334
+ const currentStatus =
3335
+ currentStage[
3336
+ stageConfig
3337
+ .toggleConfig
3338
+ .statusField
3339
+ ];
3340
+ isStageComplete =
3341
+ currentStatus ===
3342
+ stageConfig.toggleConfig
3343
+ .activeStatusValue;
3344
+
3192
3345
  // Debug logging
3193
- console.log('Stage completion check:', {
3194
- stageId: stage.id,
3195
- stageLabel: stage.label,
3196
- currentStatus,
3197
- activeStatusValue: stageConfig.toggleConfig.activeStatusValue,
3198
- isStageComplete
3199
- });
3346
+ console.log(
3347
+ 'Stage completion check:',
3348
+ {
3349
+ stageId: stage.id,
3350
+ stageLabel:
3351
+ stage.label,
3352
+ currentStatus,
3353
+ activeStatusValue:
3354
+ stageConfig
3355
+ .toggleConfig
3356
+ .activeStatusValue,
3357
+ isStageComplete,
3358
+ }
3359
+ );
3200
3360
  }
3201
3361
  }
3202
3362
  } else {
3203
3363
  // Legacy stage completion check
3204
- isStageComplete = data[stages.key] >= stage.id;
3364
+ isStageComplete =
3365
+ data[stages.key] >= stage.id;
3205
3366
  }
3206
3367
 
3207
3368
  // Get color for stageCounter type
3208
3369
  let stageColor = null;
3209
- if (stages.type === 'stageCounter' && stages.stageConfig?.stageColour) {
3210
- const stageDataArray = data[stages.stageConfig.key] || [];
3211
- const currentStage = stageDataArray.find(s => s.id === stage.id);
3212
- if (currentStage) {
3213
- const currentStatus = currentStage[stages.stageConfig.stageColour.key];
3214
- const colorOption = stages.stageConfig.stageColour.options?.find(
3215
- opt => opt.id === currentStatus
3370
+ if (
3371
+ stages.type === 'stageCounter' &&
3372
+ stages.stageConfig?.stageColour
3373
+ ) {
3374
+ const stageDataArray =
3375
+ data[stages.stageConfig.key] ||
3376
+ [];
3377
+ const currentStage =
3378
+ stageDataArray.find(
3379
+ (s) => s.id === stage.id
3216
3380
  );
3217
- stageColor = colorOption?.colour;
3381
+ if (currentStage) {
3382
+ const currentStatus =
3383
+ currentStage[
3384
+ stages.stageConfig
3385
+ .stageColour.key
3386
+ ];
3387
+ const colorOption =
3388
+ stages.stageConfig.stageColour.options?.find(
3389
+ (opt) =>
3390
+ opt.id ===
3391
+ currentStatus
3392
+ );
3393
+ stageColor =
3394
+ colorOption?.colour;
3218
3395
  }
3219
3396
  }
3220
3397
 
@@ -3226,11 +3403,21 @@ function GenericDetail({
3226
3403
  ? `${styles['opp-complete']}`
3227
3404
  : ''
3228
3405
  }
3229
- style={stageColor ? {
3230
- borderColor: stageColor,
3231
- color: isStageComplete ? '#fff' : stageColor,
3232
- backgroundColor: isStageComplete ? stageColor : 'transparent'
3233
- } : {}}
3406
+ style={
3407
+ stageColor
3408
+ ? {
3409
+ borderColor:
3410
+ stageColor,
3411
+ color: isStageComplete
3412
+ ? '#fff'
3413
+ : stageColor,
3414
+ backgroundColor:
3415
+ isStageComplete
3416
+ ? stageColor
3417
+ : 'transparent',
3418
+ }
3419
+ : {}
3420
+ }
3234
3421
  onClick={(e) => {
3235
3422
  e.preventDefault();
3236
3423
 
@@ -3240,15 +3427,27 @@ function GenericDetail({
3240
3427
  );
3241
3428
  }}
3242
3429
  >
3243
- <a style={stageColor ? {
3244
- color: isStageComplete ? '#fff' : stageColor
3245
- } : {}}>
3430
+ <a
3431
+ style={
3432
+ stageColor
3433
+ ? {
3434
+ color: isStageComplete
3435
+ ? '#fff'
3436
+ : stageColor,
3437
+ }
3438
+ : {}
3439
+ }
3440
+ >
3246
3441
  {stage.label}
3247
3442
  {isStageComplete && (
3248
3443
  <CircleCheck
3249
3444
  strokeWidth={2}
3250
3445
  size={16}
3251
- color={stageColor ? '#fff' : undefined}
3446
+ color={
3447
+ stageColor
3448
+ ? '#fff'
3449
+ : undefined
3450
+ }
3252
3451
  />
3253
3452
  )}
3254
3453
  </a>