@visns-studio/visns-components 5.0.3 → 5.0.5
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/AsyncSelect.jsx +121 -15
- package/src/components/crm/DataGrid.jsx +25 -5
- package/src/components/crm/Field.jsx +163 -147
- package/src/components/crm/Form.jsx +19 -15
- package/src/components/crm/MultiSelect.jsx +82 -26
- package/src/components/crm/auth/styles/Login.module.scss +4 -3
- package/src/components/crm/auth/styles/Profile.module.scss +55 -13
- package/src/components/crm/generic/GenericDetail.jsx +95 -14
- package/src/components/crm/generic/styles/GenericDetail.module.scss +179 -121
- package/src/components/crm/generic/styles/GenericDynamic.module.scss +27 -20
- package/src/components/crm/generic/styles/GenericIndex.module.scss +1 -1
- package/src/components/crm/styles/DataGrid.module.scss +2 -3
- package/src/components/crm/styles/Field.module.scss +13 -11
- package/src/components/crm/styles/Form.module.scss +1 -1
- package/src/components/crm/styles/Navigation.module.scss +12 -10
- package/src/components/styles/global.css +62 -3
|
@@ -215,9 +215,9 @@ function Form({
|
|
|
215
215
|
const handleSelectOption = () => {
|
|
216
216
|
updateFormData({ [id]: inputValue });
|
|
217
217
|
|
|
218
|
-
// Update any matching form data properties
|
|
219
|
-
Object.keys(inputValue).forEach((key) => {
|
|
220
|
-
if (formData.hasOwnProperty(key)) {
|
|
218
|
+
// Update any matching form data properties except for the second item
|
|
219
|
+
Object.keys(inputValue).forEach((key, index) => {
|
|
220
|
+
if (index !== 1 && formData.hasOwnProperty(key)) {
|
|
221
221
|
updateFormData({ [key]: inputValue[key] });
|
|
222
222
|
}
|
|
223
223
|
});
|
|
@@ -287,6 +287,7 @@ function Form({
|
|
|
287
287
|
// Switch cases to handle different actions
|
|
288
288
|
switch (action.action) {
|
|
289
289
|
case 'select-option':
|
|
290
|
+
case 'create-option': // New case to handle create-option
|
|
290
291
|
handleSelectOption();
|
|
291
292
|
break;
|
|
292
293
|
case 'clear':
|
|
@@ -584,44 +585,44 @@ function Form({
|
|
|
584
585
|
|
|
585
586
|
if (['create', 'update'].includes(formType)) {
|
|
586
587
|
fields.forEach((item) => {
|
|
588
|
+
// Skip createOnly fields if formType is 'update'
|
|
587
589
|
if (formType === 'update' && item.createOnly) {
|
|
588
590
|
return;
|
|
589
591
|
}
|
|
590
592
|
|
|
593
|
+
// Check if the item is required
|
|
591
594
|
if (item.required) {
|
|
592
|
-
if
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
) {
|
|
595
|
+
// Check if formData[item.id] is null, undefined, or an empty string
|
|
596
|
+
if (!formData[item.id]) {
|
|
597
|
+
// Check for required_check dependency
|
|
596
598
|
if (
|
|
597
|
-
item.hasOwnProperty('required_check') &&
|
|
598
599
|
item.required_check &&
|
|
599
600
|
!formData[item.required_check]
|
|
600
601
|
) {
|
|
601
602
|
validation += `${item.label} is a required field.<br/>`;
|
|
602
603
|
_inputClass[item.id] = styles.inputError;
|
|
603
604
|
} else {
|
|
604
|
-
console.info('b', formData, item.id);
|
|
605
605
|
validation += `${item.label} is a required field.<br/>`;
|
|
606
606
|
_inputClass[item.id] = styles.inputError;
|
|
607
607
|
}
|
|
608
608
|
} else {
|
|
609
|
-
_inputClass[item.id] = '';
|
|
609
|
+
_inputClass[item.id] = ''; // Clear error class if valid
|
|
610
610
|
}
|
|
611
611
|
} else if (
|
|
612
|
-
item.hasOwnProperty('required_rely') &&
|
|
613
612
|
item.required_rely &&
|
|
614
613
|
formData[item.required_rely] &&
|
|
615
|
-
|
|
616
|
-
formData[item.id] === '')
|
|
614
|
+
!formData[item.id]
|
|
617
615
|
) {
|
|
616
|
+
// Check if the dependent required field is empty when it should be filled
|
|
618
617
|
validation += `${item.label} is a required field.<br/>`;
|
|
619
618
|
_inputClass[item.id] = styles.inputError;
|
|
620
619
|
} else {
|
|
621
|
-
_inputClass[item.id] = '';
|
|
620
|
+
_inputClass[item.id] = ''; // Clear error class if not required
|
|
622
621
|
}
|
|
623
622
|
});
|
|
624
623
|
|
|
624
|
+
console.info(_inputClass);
|
|
625
|
+
|
|
625
626
|
setInputClass(_inputClass);
|
|
626
627
|
}
|
|
627
628
|
|
|
@@ -1371,7 +1372,10 @@ function Form({
|
|
|
1371
1372
|
<div className={styles.modal}>
|
|
1372
1373
|
<div className={styles.modal__header}>
|
|
1373
1374
|
<h1>{formSettings[formType]?.title}</h1>
|
|
1374
|
-
<button
|
|
1375
|
+
<button
|
|
1376
|
+
className={styles.modal__close}
|
|
1377
|
+
onClick={closeModal}
|
|
1378
|
+
>
|
|
1375
1379
|
<CircleX strokeWidth={2} size={24} />
|
|
1376
1380
|
</button>
|
|
1377
1381
|
</div>
|
|
@@ -1,54 +1,109 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import CustomFetch from './Fetch';
|
|
2
3
|
import SelectList from './SelectList';
|
|
3
|
-
import Select, { createFilter } from 'react-select';
|
|
4
|
+
import Select, { createFilter, components } from 'react-select';
|
|
5
|
+
import CreatableSelect from 'react-select/creatable';
|
|
4
6
|
import styles from './styles/AsyncSelect.module.scss';
|
|
5
7
|
|
|
8
|
+
// Custom Option component to add a second line of text
|
|
9
|
+
const CustomOption = (props) => (
|
|
10
|
+
<components.Option {...props}>
|
|
11
|
+
<div>
|
|
12
|
+
{props.data.label}
|
|
13
|
+
{props.data.description && props.data.description !== '' && (
|
|
14
|
+
<div style={{ fontSize: '0.85em', color: '#888' }}>
|
|
15
|
+
{props.data.description}
|
|
16
|
+
</div>
|
|
17
|
+
)}
|
|
18
|
+
</div>
|
|
19
|
+
</components.Option>
|
|
20
|
+
);
|
|
21
|
+
|
|
6
22
|
function MultiSelect({
|
|
7
23
|
className,
|
|
8
|
-
inputValue,
|
|
24
|
+
inputValue = [],
|
|
9
25
|
multi,
|
|
10
26
|
onChange,
|
|
11
|
-
options,
|
|
27
|
+
options = [],
|
|
12
28
|
placeholder,
|
|
13
29
|
settings,
|
|
14
30
|
style,
|
|
31
|
+
isCreatable = false,
|
|
32
|
+
creatableConfig = {},
|
|
15
33
|
}) {
|
|
16
34
|
const [selectOptions, setSelectOptions] = useState([]);
|
|
35
|
+
const [selectValue, setSelectValue] = useState([]);
|
|
17
36
|
|
|
18
37
|
useEffect(() => {
|
|
19
|
-
|
|
38
|
+
const _options = Array.isArray(options)
|
|
39
|
+
? options.map((a) => ({
|
|
40
|
+
value: a.id,
|
|
41
|
+
label: a.label || a.name || a.description || 'Unknown',
|
|
42
|
+
description: a.description || '', // Include description field
|
|
43
|
+
...a,
|
|
44
|
+
}))
|
|
45
|
+
: [];
|
|
46
|
+
setSelectOptions(_options);
|
|
47
|
+
}, [options]);
|
|
20
48
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const normalizeInputValue = (value) => {
|
|
51
|
+
// Ensure value is an array, wrapping single items if necessary
|
|
52
|
+
return Array.isArray(value) ? value : value ? [value] : [];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const _values = normalizeInputValue(inputValue).map((a) => ({
|
|
56
|
+
value: a.id,
|
|
57
|
+
label: a.label || a.name || a.description || 'Unknown',
|
|
58
|
+
description: a.description || '', // Include description field
|
|
59
|
+
...a,
|
|
60
|
+
}));
|
|
27
61
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
_optionItem = {
|
|
31
|
-
..._optionItem,
|
|
32
|
-
[b]: a[b],
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
});
|
|
62
|
+
setSelectValue(_values);
|
|
63
|
+
}, [inputValue]);
|
|
36
64
|
|
|
37
|
-
|
|
38
|
-
|
|
65
|
+
const handleCreate = async (inputValue) => {
|
|
66
|
+
if (creatableConfig.url && creatableConfig.method) {
|
|
67
|
+
const res = await CustomFetch(
|
|
68
|
+
creatableConfig.url,
|
|
69
|
+
creatableConfig.method,
|
|
70
|
+
{ label: inputValue }
|
|
71
|
+
);
|
|
39
72
|
|
|
40
|
-
|
|
73
|
+
if (res && res.data) {
|
|
74
|
+
const newOption = {
|
|
75
|
+
value: res.data.id,
|
|
76
|
+
label: res.data.label || inputValue,
|
|
77
|
+
description: res.data.description || 'Newly created item', // Default description for new items
|
|
78
|
+
...res.data,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Update options and trigger onChange with the new option
|
|
82
|
+
setSelectOptions((prevOptions) => [...prevOptions, newOption]);
|
|
83
|
+
setSelectValue((prevValues) =>
|
|
84
|
+
multi ? [...prevValues, newOption] : [newOption]
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
// Trigger onChange with the new option
|
|
88
|
+
onChange(
|
|
89
|
+
multi ? [...selectValue, newOption] : newOption,
|
|
90
|
+
{ action: 'create-option' },
|
|
91
|
+
settings.id
|
|
92
|
+
);
|
|
93
|
+
}
|
|
41
94
|
}
|
|
42
|
-
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const SelectComponent = isCreatable ? CreatableSelect : Select;
|
|
43
98
|
|
|
44
99
|
return (
|
|
45
|
-
<
|
|
46
|
-
closeMenuOnSelect={multi
|
|
100
|
+
<SelectComponent
|
|
101
|
+
closeMenuOnSelect={!multi}
|
|
47
102
|
isClearable
|
|
48
103
|
isSearchable
|
|
49
104
|
isMulti={multi}
|
|
50
105
|
filterOption={createFilter({ ignoreAccents: false })}
|
|
51
|
-
components={{
|
|
106
|
+
components={{ Option: CustomOption }} // Use the custom Option component
|
|
52
107
|
options={selectOptions}
|
|
53
108
|
onChange={(inputValue, action) => {
|
|
54
109
|
onChange(inputValue, action, settings.id);
|
|
@@ -69,7 +124,8 @@ function MultiSelect({
|
|
|
69
124
|
: '52px',
|
|
70
125
|
}),
|
|
71
126
|
}}
|
|
72
|
-
value={
|
|
127
|
+
value={selectValue}
|
|
128
|
+
onCreateOption={isCreatable ? handleCreate : undefined} // Only add if creatable
|
|
73
129
|
placeholder={placeholder ? placeholder : 'Select...'}
|
|
74
130
|
/>
|
|
75
131
|
);
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
.fi__span {
|
|
13
13
|
position: absolute;
|
|
14
14
|
transition: all 200ms;
|
|
15
|
-
opacity:
|
|
15
|
+
opacity: 1;
|
|
16
16
|
left: 0;
|
|
17
17
|
transform-origin: top left;
|
|
18
18
|
cursor: text;
|
|
@@ -164,10 +164,11 @@
|
|
|
164
164
|
input:not(:placeholder-shown) + .fi__span,
|
|
165
165
|
textarea:not(:placeholder-shown) + .fi__span,
|
|
166
166
|
select:not(:placeholder-shown) + .fi__span {
|
|
167
|
-
transform: translateY(-
|
|
167
|
+
transform: translateY(-135%) translateX(10px) scale(0.9);
|
|
168
168
|
opacity: 1;
|
|
169
169
|
padding: 0;
|
|
170
170
|
background: var(--bg-color);
|
|
171
|
-
font-weight:
|
|
171
|
+
font-weight: 400;
|
|
172
172
|
transition: all 0.5s cubic-bezier(0.5, 0.5, 0, 1);
|
|
173
|
+
color: rgba(var(--paragraph-rgb), 0.65) !important;
|
|
173
174
|
}
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
input:not(:placeholder-shown) + .fi__span,
|
|
4
4
|
textarea:not(:placeholder-shown) + .fi__span,
|
|
5
5
|
select:not(:placeholder-shown) + .fi__span {
|
|
6
|
-
transform: translateY(-40%) translateX(10px) scale(0.
|
|
6
|
+
transform: translateY(-40%) translateX(10px) scale(0.9);
|
|
7
7
|
opacity: 1;
|
|
8
|
-
padding: 0
|
|
8
|
+
padding: 0 0.25rem;
|
|
9
9
|
background: var(--tertiary-color);
|
|
10
|
-
font-weight:
|
|
10
|
+
font-weight: 400;
|
|
11
11
|
border-radius: var(--br);
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -111,30 +111,72 @@ select:not(:placeholder-shown) + .fi__span {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
.grid__subcontent {
|
|
114
|
-
flex: 0 1
|
|
115
|
-
background:
|
|
114
|
+
flex: 0 1 80%;
|
|
115
|
+
background: var(--tertiary-color);
|
|
116
116
|
border-radius: var(--br);
|
|
117
|
-
border: 1px solid var(--
|
|
117
|
+
border: 1px solid rgba(var(--primary-rgb), 0.05);
|
|
118
118
|
box-sizing: border-box;
|
|
119
|
-
padding:
|
|
120
|
-
margin:
|
|
119
|
+
padding: 2px;
|
|
120
|
+
margin: 8px 0;
|
|
121
121
|
height: auto;
|
|
122
122
|
position: relative;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
.grid__subcontent h2 {
|
|
126
|
+
color: var(--primary-color);
|
|
127
|
+
font-size: 1.35em;
|
|
128
|
+
padding: 0;
|
|
129
|
+
margin: 0 0 30px 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
125
132
|
.gridtxt__header {
|
|
126
133
|
width: 100%;
|
|
127
134
|
display: block;
|
|
128
|
-
background: var(--
|
|
135
|
+
background: rgba(var(--primary-rgb), 0.05);
|
|
129
136
|
box-sizing: border-box;
|
|
130
137
|
padding: 10px 20px;
|
|
131
|
-
border-
|
|
132
|
-
border-top-right-radius: var(--br);
|
|
138
|
+
border-radius: var(--br);
|
|
133
139
|
margin-bottom: 0;
|
|
134
140
|
font-weight: 700;
|
|
135
141
|
text-align: center;
|
|
136
142
|
color: var(--paragraph-color);
|
|
137
|
-
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.gridtxt__header span {
|
|
146
|
+
font-weight: 700;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.gridtxt > ul {
|
|
150
|
+
width: 100%;
|
|
151
|
+
display: flex;
|
|
152
|
+
justify-content: flex-start;
|
|
153
|
+
flex-direction: row;
|
|
154
|
+
flex-wrap: wrap;
|
|
155
|
+
list-style: none;
|
|
156
|
+
box-sizing: border-box;
|
|
157
|
+
padding: 0.85rem;
|
|
158
|
+
margin: 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.gridtxt > ul li {
|
|
162
|
+
flex: 0 0 calc(50% - 10px);
|
|
163
|
+
padding: 0.85rem;
|
|
164
|
+
box-sizing: border-box;
|
|
165
|
+
border: 1px solid rgba(var(--primary-rgb), 0.095);
|
|
166
|
+
color: var(--paragraph-color);
|
|
167
|
+
background: rgba(var(--primary-rgb), 0.015);
|
|
168
|
+
margin: 5px;
|
|
169
|
+
border-radius: var(--br);
|
|
170
|
+
line-height: 1.45;
|
|
171
|
+
font-size: 1rem;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.gridtxt > ul .fw-grid-item {
|
|
175
|
+
flex: 0 0 calc(100% - 10px);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.gridtxt > ul .notecolor {
|
|
179
|
+
border: 1px solid var(--secondary-color);
|
|
138
180
|
}
|
|
139
181
|
|
|
140
182
|
.formcontainer {
|
|
@@ -277,7 +319,7 @@ select:not(:placeholder-shown) + .fi__span {
|
|
|
277
319
|
a {
|
|
278
320
|
text-decoration: none;
|
|
279
321
|
color: var(--secondary-color);
|
|
280
|
-
font-weight:
|
|
322
|
+
font-weight: 400;
|
|
281
323
|
}
|
|
282
324
|
|
|
283
325
|
svg {
|
|
@@ -10,6 +10,7 @@ import Dropzone from 'react-dropzone';
|
|
|
10
10
|
import { confirmAlert } from 'react-confirm-alert';
|
|
11
11
|
import truncate from 'truncate';
|
|
12
12
|
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
|
13
|
+
import { saveAs } from 'file-saver';
|
|
13
14
|
import { toast } from 'react-toastify';
|
|
14
15
|
import {
|
|
15
16
|
CircleCheck,
|
|
@@ -75,6 +76,8 @@ function GenericDetail({
|
|
|
75
76
|
if (activeTab) {
|
|
76
77
|
const activeTabConfig = tabs.find((t) => t.id === activeTab.id);
|
|
77
78
|
|
|
79
|
+
console.info(tabs);
|
|
80
|
+
|
|
78
81
|
if (type === 'view') {
|
|
79
82
|
setActiveStage(stage.id);
|
|
80
83
|
} else {
|
|
@@ -95,6 +98,32 @@ function GenericDetail({
|
|
|
95
98
|
|
|
96
99
|
if (res.data.error === '') {
|
|
97
100
|
handleReload();
|
|
101
|
+
toast.success('Stage updated successfully');
|
|
102
|
+
} else {
|
|
103
|
+
toast.error(res.data.error);
|
|
104
|
+
}
|
|
105
|
+
} else if (
|
|
106
|
+
stages &&
|
|
107
|
+
stages.url &&
|
|
108
|
+
stages.url !== '' &&
|
|
109
|
+
stages.key &&
|
|
110
|
+
stages.key !== '' &&
|
|
111
|
+
stages.urlParam &&
|
|
112
|
+
stages.urlParam !== ''
|
|
113
|
+
) {
|
|
114
|
+
const url = `${stages.url}/${routeParams[urlParam]}`;
|
|
115
|
+
|
|
116
|
+
const res = await CustomFetch(
|
|
117
|
+
url,
|
|
118
|
+
stages.method || 'POST',
|
|
119
|
+
{
|
|
120
|
+
[stages.key]: stage.id,
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
if (res.data.error === '') {
|
|
125
|
+
handleReload();
|
|
126
|
+
toast.success('Stage updated successfully');
|
|
98
127
|
} else {
|
|
99
128
|
toast.error(res.data.error);
|
|
100
129
|
}
|
|
@@ -436,10 +465,32 @@ function GenericDetail({
|
|
|
436
465
|
if (activeTabConfig && activeTabConfig.type) {
|
|
437
466
|
switch (activeTabConfig.type) {
|
|
438
467
|
case 'dropzone':
|
|
439
|
-
const downloadFile = (
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
468
|
+
const downloadFile = (file) => {
|
|
469
|
+
const { id, file_url, file_name } = file;
|
|
470
|
+
|
|
471
|
+
// If file_url exists, use it for the download, else use the id to download
|
|
472
|
+
const urlToDownload =
|
|
473
|
+
file_url || `/ajax/files/download/${id}`;
|
|
474
|
+
|
|
475
|
+
// Use fetch to retrieve the file as a blob
|
|
476
|
+
fetch(urlToDownload, {
|
|
477
|
+
method: 'GET',
|
|
478
|
+
headers: {
|
|
479
|
+
'Content-Type':
|
|
480
|
+
'application/octet-stream', // Specify content type as binary
|
|
481
|
+
},
|
|
482
|
+
})
|
|
483
|
+
.then((response) => response.blob()) // Convert response to Blob
|
|
484
|
+
.then((blob) => {
|
|
485
|
+
// Use FileSaver to download the file with the specified name
|
|
486
|
+
saveAs(blob, file_name);
|
|
487
|
+
})
|
|
488
|
+
.catch((error) => {
|
|
489
|
+
console.error(
|
|
490
|
+
'Error downloading the file:',
|
|
491
|
+
error
|
|
492
|
+
);
|
|
493
|
+
});
|
|
443
494
|
};
|
|
444
495
|
|
|
445
496
|
const deleteFile = (e, id, name) => {
|
|
@@ -521,9 +572,13 @@ function GenericDetail({
|
|
|
521
572
|
key: response.key,
|
|
522
573
|
bucket: response.bucket,
|
|
523
574
|
filename: file.name,
|
|
575
|
+
file_name: file.name,
|
|
524
576
|
filesize: file.size,
|
|
577
|
+
file_size: file.size,
|
|
525
578
|
extension:
|
|
526
579
|
response.extension,
|
|
580
|
+
file_extension:
|
|
581
|
+
response.extension,
|
|
527
582
|
file_relationship:
|
|
528
583
|
activeTabConfig.file_relationship,
|
|
529
584
|
};
|
|
@@ -533,9 +588,30 @@ function GenericDetail({
|
|
|
533
588
|
'PUT',
|
|
534
589
|
requestBody,
|
|
535
590
|
(res) => {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
591
|
+
if (
|
|
592
|
+
activeTabConfig.file_relationship &&
|
|
593
|
+
res.data[
|
|
594
|
+
activeTabConfig
|
|
595
|
+
.file_relationship
|
|
596
|
+
]
|
|
597
|
+
) {
|
|
598
|
+
setFiles(
|
|
599
|
+
res.data[
|
|
600
|
+
activeTabConfig
|
|
601
|
+
.file_relationship
|
|
602
|
+
]
|
|
603
|
+
);
|
|
604
|
+
} else {
|
|
605
|
+
if (
|
|
606
|
+
res?.data
|
|
607
|
+
?.files
|
|
608
|
+
) {
|
|
609
|
+
setFiles(
|
|
610
|
+
res.data
|
|
611
|
+
.files
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
539
615
|
}
|
|
540
616
|
);
|
|
541
617
|
});
|
|
@@ -635,7 +711,7 @@ function GenericDetail({
|
|
|
635
711
|
<li
|
|
636
712
|
onClick={() => {
|
|
637
713
|
downloadFile(
|
|
638
|
-
a
|
|
714
|
+
a
|
|
639
715
|
);
|
|
640
716
|
}}
|
|
641
717
|
key={'tf-' + b}
|
|
@@ -1377,8 +1453,17 @@ function GenericDetail({
|
|
|
1377
1453
|
|
|
1378
1454
|
tabs.forEach((tab) => {
|
|
1379
1455
|
if (tab.type && tab.type === 'dropzone') {
|
|
1380
|
-
if (
|
|
1381
|
-
|
|
1456
|
+
if (
|
|
1457
|
+
tab.file_relationship &&
|
|
1458
|
+
tab.file_relationship !== ''
|
|
1459
|
+
) {
|
|
1460
|
+
if (res[tab.file_relationship]) {
|
|
1461
|
+
setFiles(res[tab.file_relationship]);
|
|
1462
|
+
}
|
|
1463
|
+
} else {
|
|
1464
|
+
if (res.files) {
|
|
1465
|
+
setFiles(res.files);
|
|
1466
|
+
}
|
|
1382
1467
|
}
|
|
1383
1468
|
}
|
|
1384
1469
|
});
|
|
@@ -1454,10 +1539,6 @@ function GenericDetail({
|
|
|
1454
1539
|
<div className={styles.oppstatus}>
|
|
1455
1540
|
<ul className={styles.opppath}>
|
|
1456
1541
|
{stages.data.map((stage) => {
|
|
1457
|
-
if (!stage.id) {
|
|
1458
|
-
return null; // Skip rendering this stage
|
|
1459
|
-
}
|
|
1460
|
-
|
|
1461
1542
|
const isStageComplete =
|
|
1462
1543
|
data[stages.key] >= stage.id;
|
|
1463
1544
|
|