@visns-studio/visns-components 5.12.2 → 5.12.4
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 +2 -2
- package/package.json +1 -1
- package/src/components/DataGrid.jsx +95 -51
- package/src/components/columns/ColumnRenderers.jsx +322 -20
- package/src/components/generic/GenericDetail.jsx +200 -3
- package/src/components/generic/GenericIndex.jsx +31 -18
- package/src/components/generic/StagePopupModal.jsx +451 -0
- package/src/components/styles/StagePopupModal.module.scss +234 -0
- package/src/components/utils/ConfirmDialog.js +1 -1
- package/src/index.js +2 -0
|
@@ -71,6 +71,7 @@ import GenericEditableTable from './GenericEditableTable';
|
|
|
71
71
|
import MergeEntity from '../MergeEntity';
|
|
72
72
|
import MultiSelect from '../MultiSelect';
|
|
73
73
|
import QrCode from '../QrCode';
|
|
74
|
+
import StagePopupModal from './StagePopupModal';
|
|
74
75
|
import Table from '../DataGrid';
|
|
75
76
|
import TableFilter from '../TableFilter';
|
|
76
77
|
|
|
@@ -157,6 +158,10 @@ function GenericDetail({
|
|
|
157
158
|
const [total, setTotal] = useState(0);
|
|
158
159
|
const [windowHeight, setWindowHeight] = useState(window.innerHeight);
|
|
159
160
|
|
|
161
|
+
/** Stage Popup States */
|
|
162
|
+
const [showStagePopup, setShowStagePopup] = useState(false);
|
|
163
|
+
const [stagePopupData, setStagePopupData] = useState(null);
|
|
164
|
+
|
|
160
165
|
/** Ocr States */
|
|
161
166
|
const [ocrRowsSelected, setOcrRowsSelected] = useState('');
|
|
162
167
|
|
|
@@ -297,6 +302,45 @@ function GenericDetail({
|
|
|
297
302
|
};
|
|
298
303
|
|
|
299
304
|
const handleStageUpdate = async (stage, type) => {
|
|
305
|
+
try {
|
|
306
|
+
// Check if stage has popup configuration
|
|
307
|
+
if (stage.popup?.enabled) {
|
|
308
|
+
setStagePopupData({ stage, type });
|
|
309
|
+
setShowStagePopup(true);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// If no popup, proceed with normal stage update
|
|
314
|
+
await performStageUpdate(stage, type);
|
|
315
|
+
} catch (error) {
|
|
316
|
+
console.error(error);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
const handleStagePopupConfirm = async (popupFormData) => {
|
|
321
|
+
try {
|
|
322
|
+
setShowStagePopup(false);
|
|
323
|
+
|
|
324
|
+
if (!stagePopupData) return;
|
|
325
|
+
|
|
326
|
+
const { stage, type } = stagePopupData;
|
|
327
|
+
|
|
328
|
+
// Proceed with normal stage update, but include popup form data
|
|
329
|
+
await performStageUpdate(stage, type, popupFormData);
|
|
330
|
+
|
|
331
|
+
setStagePopupData(null);
|
|
332
|
+
} catch (error) {
|
|
333
|
+
console.error('Stage popup confirm error:', error);
|
|
334
|
+
toast.error('Failed to update stage');
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const handleStagePopupCancel = () => {
|
|
339
|
+
setShowStagePopup(false);
|
|
340
|
+
setStagePopupData(null);
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const performStageUpdate = async (stage, type, additionalData = {}) => {
|
|
300
344
|
try {
|
|
301
345
|
const activeTab = subnav.find((s) => s.show === true);
|
|
302
346
|
|
|
@@ -305,7 +349,102 @@ function GenericDetail({
|
|
|
305
349
|
|
|
306
350
|
if (type === 'view') {
|
|
307
351
|
setActiveStage(stage.id);
|
|
352
|
+
} else if (type === 'stageCounter') {
|
|
353
|
+
// Handle new stageCounter functionality
|
|
354
|
+
console.log('performStageUpdate called with stageCounter type:', { stage, data, stages });
|
|
355
|
+
|
|
356
|
+
const stageConfig = stages.stageConfig || activeTabConfig.stages?.stageConfig;
|
|
357
|
+
if (!stageConfig?.toggleable || !stageConfig?.toggleConfig) {
|
|
358
|
+
console.warn('Stage is not configured for toggling', { stageConfig });
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Get current stage data
|
|
363
|
+
const stageDataArray = data[stageConfig.key] || [];
|
|
364
|
+
const currentStage = stageDataArray.find(s => s.id === stage.id);
|
|
365
|
+
|
|
366
|
+
if (!currentStage) {
|
|
367
|
+
console.warn('Stage not found in data');
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Determine current and target status
|
|
372
|
+
const currentStatus = currentStage[stageConfig.toggleConfig.statusField];
|
|
373
|
+
const isActive = currentStatus === stageConfig.toggleConfig.activeStatusValue;
|
|
374
|
+
const targetStatus = isActive
|
|
375
|
+
? stageConfig.toggleConfig.inactiveStatusValue
|
|
376
|
+
: stageConfig.toggleConfig.activeStatusValue;
|
|
377
|
+
|
|
378
|
+
// Build API URL
|
|
379
|
+
let url = stageConfig.toggleConfig.updateUrl;
|
|
380
|
+
url = url.replace('{id}', routeParams[stages.urlParam]);
|
|
381
|
+
url = url.replace('{stage_id}', stage.id);
|
|
382
|
+
|
|
383
|
+
// Make API call with additional data from popup
|
|
384
|
+
const res = await CustomFetch(
|
|
385
|
+
url,
|
|
386
|
+
stageConfig.toggleConfig.updateMethod || 'PUT',
|
|
387
|
+
{
|
|
388
|
+
[stageConfig.toggleConfig.updateKey]: targetStatus,
|
|
389
|
+
...additionalData
|
|
390
|
+
}
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
if (res.data.error === '') {
|
|
394
|
+
handleReload();
|
|
395
|
+
const actionText = isActive ? 'disabled' : 'enabled';
|
|
396
|
+
toast.success(`Stage "${stage.label}" ${actionText} successfully`);
|
|
397
|
+
} else {
|
|
398
|
+
toast.error(res.data.error);
|
|
399
|
+
}
|
|
400
|
+
} else if (type === 'legacy_toggle') {
|
|
401
|
+
// Handle legacy toggle functionality - toggle between current stage and previous
|
|
402
|
+
console.log('performStageUpdate called with legacy_toggle type:', { stage, data, stages });
|
|
403
|
+
|
|
404
|
+
if (
|
|
405
|
+
stages &&
|
|
406
|
+
stages.url &&
|
|
407
|
+
stages.url !== '' &&
|
|
408
|
+
stages.key &&
|
|
409
|
+
stages.key !== '' &&
|
|
410
|
+
stages.urlParam &&
|
|
411
|
+
stages.urlParam !== ''
|
|
412
|
+
) {
|
|
413
|
+
const currentStatus = data[stages.key];
|
|
414
|
+
let targetStatus;
|
|
415
|
+
|
|
416
|
+
// If clicking on the current stage (completed), move back one step
|
|
417
|
+
// If clicking on a future stage, move to that stage
|
|
418
|
+
if (currentStatus >= stage.id) {
|
|
419
|
+
// Stage is completed, move back one step (or to 0 if clicking stage 1)
|
|
420
|
+
targetStatus = stage.id > 1 ? stage.id - 1 : 0;
|
|
421
|
+
} else {
|
|
422
|
+
// Stage is not completed, move to this stage
|
|
423
|
+
targetStatus = stage.id;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const url = `${stages.url}/${routeParams[stages.urlParam]}`;
|
|
427
|
+
|
|
428
|
+
// Include additional data from popup
|
|
429
|
+
const res = await CustomFetch(
|
|
430
|
+
url,
|
|
431
|
+
stages.method || 'PUT',
|
|
432
|
+
{
|
|
433
|
+
[stages.key]: targetStatus,
|
|
434
|
+
...additionalData
|
|
435
|
+
}
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
if (res.data.error === '') {
|
|
439
|
+
handleReload();
|
|
440
|
+
const actionText = currentStatus >= stage.id ? 'moved back from' : 'progressed to';
|
|
441
|
+
toast.success(`Stage ${actionText} "${stage.label}" successfully`);
|
|
442
|
+
} else {
|
|
443
|
+
toast.error(res.data.error);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
308
446
|
} else {
|
|
447
|
+
// Handle legacy stage functionality
|
|
309
448
|
if (
|
|
310
449
|
activeTabConfig.stages &&
|
|
311
450
|
activeTabConfig.stages.url &&
|
|
@@ -319,6 +458,7 @@ function GenericDetail({
|
|
|
319
458
|
|
|
320
459
|
const res = await CustomFetch(url, 'POST', {
|
|
321
460
|
[activeTabConfig.stages.key]: stage.id,
|
|
461
|
+
...additionalData
|
|
322
462
|
});
|
|
323
463
|
|
|
324
464
|
if (res.data.error === '') {
|
|
@@ -343,6 +483,7 @@ function GenericDetail({
|
|
|
343
483
|
stages.method || 'POST',
|
|
344
484
|
{
|
|
345
485
|
[stages.key]: stage.id,
|
|
486
|
+
...additionalData
|
|
346
487
|
}
|
|
347
488
|
);
|
|
348
489
|
|
|
@@ -2878,8 +3019,46 @@ function GenericDetail({
|
|
|
2878
3019
|
<div className={styles.oppstatus}>
|
|
2879
3020
|
<ul className={styles.opppath}>
|
|
2880
3021
|
{stages.data.map((stage) => {
|
|
2881
|
-
|
|
2882
|
-
|
|
3022
|
+
let isStageComplete = false;
|
|
3023
|
+
|
|
3024
|
+
if (stages.type === 'stageCounter') {
|
|
3025
|
+
// For stageCounter, check individual stage status
|
|
3026
|
+
const stageConfig = stages.stageConfig;
|
|
3027
|
+
if (stageConfig && data[stageConfig.key]) {
|
|
3028
|
+
const stageDataArray = data[stageConfig.key];
|
|
3029
|
+
const currentStage = stageDataArray.find(s => s.id === stage.id);
|
|
3030
|
+
if (currentStage && stageConfig.toggleConfig) {
|
|
3031
|
+
const currentStatus = currentStage[stageConfig.toggleConfig.statusField];
|
|
3032
|
+
isStageComplete = currentStatus === stageConfig.toggleConfig.activeStatusValue;
|
|
3033
|
+
|
|
3034
|
+
// Debug logging
|
|
3035
|
+
console.log('Stage completion check:', {
|
|
3036
|
+
stageId: stage.id,
|
|
3037
|
+
stageLabel: stage.label,
|
|
3038
|
+
currentStatus,
|
|
3039
|
+
activeStatusValue: stageConfig.toggleConfig.activeStatusValue,
|
|
3040
|
+
isStageComplete
|
|
3041
|
+
});
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
} else {
|
|
3045
|
+
// Legacy stage completion check
|
|
3046
|
+
isStageComplete = data[stages.key] >= stage.id;
|
|
3047
|
+
}
|
|
3048
|
+
|
|
3049
|
+
// Get color for stageCounter type
|
|
3050
|
+
let stageColor = null;
|
|
3051
|
+
if (stages.type === 'stageCounter' && stages.stageConfig?.stageColour) {
|
|
3052
|
+
const stageDataArray = data[stages.stageConfig.key] || [];
|
|
3053
|
+
const currentStage = stageDataArray.find(s => s.id === stage.id);
|
|
3054
|
+
if (currentStage) {
|
|
3055
|
+
const currentStatus = currentStage[stages.stageConfig.stageColour.key];
|
|
3056
|
+
const colorOption = stages.stageConfig.stageColour.options?.find(
|
|
3057
|
+
opt => opt.id === currentStatus
|
|
3058
|
+
);
|
|
3059
|
+
stageColor = colorOption?.colour;
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
2883
3062
|
|
|
2884
3063
|
return (
|
|
2885
3064
|
<li
|
|
@@ -2889,6 +3068,11 @@ function GenericDetail({
|
|
|
2889
3068
|
? `${styles['opp-complete']}`
|
|
2890
3069
|
: ''
|
|
2891
3070
|
}
|
|
3071
|
+
style={stageColor ? {
|
|
3072
|
+
borderColor: stageColor,
|
|
3073
|
+
color: isStageComplete ? '#fff' : stageColor,
|
|
3074
|
+
backgroundColor: isStageComplete ? stageColor : 'transparent'
|
|
3075
|
+
} : {}}
|
|
2892
3076
|
onClick={(e) => {
|
|
2893
3077
|
e.preventDefault();
|
|
2894
3078
|
|
|
@@ -2898,12 +3082,15 @@ function GenericDetail({
|
|
|
2898
3082
|
);
|
|
2899
3083
|
}}
|
|
2900
3084
|
>
|
|
2901
|
-
<a
|
|
3085
|
+
<a style={stageColor ? {
|
|
3086
|
+
color: isStageComplete ? '#fff' : stageColor
|
|
3087
|
+
} : {}}>
|
|
2902
3088
|
{stage.label}
|
|
2903
3089
|
{isStageComplete && (
|
|
2904
3090
|
<CircleCheck
|
|
2905
3091
|
strokeWidth={2}
|
|
2906
3092
|
size={16}
|
|
3093
|
+
color={stageColor ? '#fff' : undefined}
|
|
2907
3094
|
/>
|
|
2908
3095
|
)}
|
|
2909
3096
|
</a>
|
|
@@ -2970,6 +3157,16 @@ function GenericDetail({
|
|
|
2970
3157
|
</div>
|
|
2971
3158
|
</div>
|
|
2972
3159
|
</Popup>
|
|
3160
|
+
|
|
3161
|
+
<StagePopupModal
|
|
3162
|
+
show={showStagePopup}
|
|
3163
|
+
stage={stagePopupData?.stage}
|
|
3164
|
+
popupConfig={stagePopupData?.stage?.popup}
|
|
3165
|
+
onConfirm={handleStagePopupConfirm}
|
|
3166
|
+
onCancel={handleStagePopupCancel}
|
|
3167
|
+
entityData={data}
|
|
3168
|
+
routeParams={routeParams}
|
|
3169
|
+
/>
|
|
2973
3170
|
</>
|
|
2974
3171
|
);
|
|
2975
3172
|
}
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import '../styles/global.css';
|
|
2
2
|
|
|
3
|
-
import React, {
|
|
4
|
-
useCallback,
|
|
5
|
-
useEffect,
|
|
6
|
-
useRef,
|
|
7
|
-
useState,
|
|
8
|
-
} from 'react';
|
|
3
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
9
4
|
import { useParams, Outlet } from 'react-router-dom';
|
|
10
5
|
import { toast } from 'react-toastify';
|
|
11
6
|
import { saveAs } from 'file-saver';
|
|
@@ -223,7 +218,6 @@ function useConfig(setting, tabs, filters, setRowsSelected, tableSetting) {
|
|
|
223
218
|
return { config, setConfig, subnav, setSubnav };
|
|
224
219
|
}
|
|
225
220
|
|
|
226
|
-
|
|
227
221
|
function GenericIndex({
|
|
228
222
|
layout = 'full',
|
|
229
223
|
setting,
|
|
@@ -494,26 +488,38 @@ function GenericIndex({
|
|
|
494
488
|
let payload = {};
|
|
495
489
|
if (config.export.payload) {
|
|
496
490
|
payload = { ...config.export.payload };
|
|
497
|
-
|
|
491
|
+
|
|
498
492
|
// Process special values in the payload
|
|
499
|
-
Object.keys(payload).forEach(key => {
|
|
493
|
+
Object.keys(payload).forEach((key) => {
|
|
500
494
|
if (payload[key] === 'currentActive()') {
|
|
501
495
|
// Get the current active filter value
|
|
502
|
-
const activeFilter = subnav?.find(filter => {
|
|
496
|
+
const activeFilter = subnav?.find((filter) => {
|
|
503
497
|
if (filter.isParent) {
|
|
504
|
-
return
|
|
498
|
+
return (
|
|
499
|
+
filter.children &&
|
|
500
|
+
filter.children.some((child) => child.show)
|
|
501
|
+
);
|
|
505
502
|
}
|
|
506
503
|
return filter.show === true;
|
|
507
504
|
});
|
|
508
505
|
|
|
509
506
|
if (activeFilter) {
|
|
510
|
-
if (
|
|
507
|
+
if (
|
|
508
|
+
activeFilter.isParent &&
|
|
509
|
+
activeFilter.children &&
|
|
510
|
+
activeFilter.children.length > 0
|
|
511
|
+
) {
|
|
511
512
|
// If it's a parent filter, get the active child's value
|
|
512
|
-
const activeChild = activeFilter.children.find(
|
|
513
|
-
|
|
513
|
+
const activeChild = activeFilter.children.find(
|
|
514
|
+
(child) => child.show
|
|
515
|
+
);
|
|
516
|
+
payload[key] = activeChild
|
|
517
|
+
? activeChild.value || activeChild.id
|
|
518
|
+
: '';
|
|
514
519
|
} else {
|
|
515
520
|
// For non-parent filters, use the filter's value or id
|
|
516
|
-
payload[key] =
|
|
521
|
+
payload[key] =
|
|
522
|
+
activeFilter.value || activeFilter.id;
|
|
517
523
|
}
|
|
518
524
|
} else {
|
|
519
525
|
// If no active filter is found, set to empty string
|
|
@@ -571,7 +577,7 @@ function GenericIndex({
|
|
|
571
577
|
{...configWithStyle}
|
|
572
578
|
ref={gridRef}
|
|
573
579
|
hoverColor={configWithStyle.style?.hoverColor}
|
|
574
|
-
gridHeight={windowHeight * 0.
|
|
580
|
+
gridHeight={windowHeight * 0.8}
|
|
575
581
|
setConfig={setConfig}
|
|
576
582
|
setTableData={setTableData}
|
|
577
583
|
setTotal={setTotal}
|
|
@@ -606,11 +612,18 @@ function GenericIndex({
|
|
|
606
612
|
const renderContentBasedOnType = useCallback(() => {
|
|
607
613
|
switch (config.type) {
|
|
608
614
|
case 'grid':
|
|
609
|
-
return
|
|
615
|
+
return (
|
|
616
|
+
<GenericGrid config={config} userProfile={userProfile} />
|
|
617
|
+
);
|
|
610
618
|
case 'table':
|
|
611
619
|
return renderTable();
|
|
612
620
|
case 'report':
|
|
613
|
-
return
|
|
621
|
+
return (
|
|
622
|
+
<GenericReportForm
|
|
623
|
+
config={config}
|
|
624
|
+
userProfile={userProfile}
|
|
625
|
+
/>
|
|
626
|
+
);
|
|
614
627
|
default:
|
|
615
628
|
return renderTable(); // Fallback to table if no type is specified or recognized
|
|
616
629
|
}
|