@visns-studio/visns-components 1.8.5 → 2.0.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.
- package/.prettierrc.json +7 -0
- package/components/cms/{visns-data-grid.jsx → DataGrid.jsx} +2 -2
- package/components/cms/DropZone.jsx +161 -0
- package/components/cms/Field.jsx +747 -0
- package/components/cms/Form.jsx +475 -0
- package/components/cms/sorting/{list.jsx → List.jsx} +3 -3
- package/components/crm/{visns-async-select.jsx → AsyncSelect.jsx} +9 -9
- package/components/crm/Call.jsx +220 -0
- package/components/crm/{visns-data-grid.jsx → DataGrid.jsx} +263 -277
- package/components/crm/{visns-field.jsx → Field.jsx} +5 -5
- package/components/crm/{visns-form.jsx → Form.jsx} +302 -205
- package/components/crm/{visns-multi-select.jsx → MultiSelect.jsx} +1 -1
- package/components/crm/{visns-navigation.jsx → Navigation.jsx} +22 -18
- package/components/crm/{visns-notification.jsx → Notification.jsx} +1 -1
- package/components/crm/TableFilter.jsx +125 -0
- package/components/crm/auth/Login.jsx +158 -0
- package/components/crm/auth/Profile.jsx +163 -0
- package/components/crm/auth/Reset.jsx +87 -0
- package/components/crm/auth/Verify.jsx +162 -0
- package/components/crm/generic/GenericDetail.jsx +380 -0
- package/components/crm/generic/GenericIndex.jsx +112 -0
- package/components/crm/generic/NotificationList.jsx +64 -0
- package/index.js +37 -19
- package/package.json +6 -6
- package/components/cms/visns-dropzone.jsx +0 -161
- package/components/cms/visns-field.jsx +0 -747
- package/components/cms/visns-form.jsx +0 -475
- package/components/crm/visns-call.jsx +0 -220
- package/components/crm/visns-table-filter.jsx +0 -153
- /package/components/cms/sorting/{item.jsx → Item.jsx} +0 -0
- /package/components/crm/{visns-autocomplete.jsx → Autocomplete.jsx} +0 -0
- /package/components/crm/{visns-download.jsx → Download.jsx} +0 -0
- /package/components/crm/{visns-fetch.jsx → Fetch.jsx} +0 -0
- /package/components/crm/{visns-loader.jsx → Loader.jsx} +0 -0
- /package/components/crm/{visns-select.jsx → Select.jsx} +0 -0
- /package/components/crm/{visns-select-list.jsx → SelectList.jsx} +0 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { useNavigate, useParams } from 'react-router-dom';
|
|
3
|
+
import { toast } from 'react-toastify';
|
|
4
|
+
import parse from 'html-react-parser';
|
|
5
|
+
import validator from 'validator';
|
|
6
|
+
import Reveal from 'react-reveal/Reveal';
|
|
7
|
+
|
|
8
|
+
import CustomFetch from '../Fetch';
|
|
9
|
+
|
|
10
|
+
const Verify = () => {
|
|
11
|
+
const navigate = useNavigate();
|
|
12
|
+
const { code } = useParams();
|
|
13
|
+
|
|
14
|
+
const [formData, setFormData] = useState({
|
|
15
|
+
password: '',
|
|
16
|
+
passwordRepeat: '',
|
|
17
|
+
passwordClass: '',
|
|
18
|
+
passwordRepeatClass: '',
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const { password, passwordRepeat, passwordClass, passwordRepeatClass } =
|
|
22
|
+
formData;
|
|
23
|
+
|
|
24
|
+
const handleInputChange = (e) => {
|
|
25
|
+
const { name, value } = e.target;
|
|
26
|
+
setFormData((prevFormData) => ({
|
|
27
|
+
...prevFormData,
|
|
28
|
+
[name]: value,
|
|
29
|
+
passwordClass: '',
|
|
30
|
+
passwordRepeatClass: '',
|
|
31
|
+
}));
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const validatePassword = () => {
|
|
35
|
+
let _error = '';
|
|
36
|
+
|
|
37
|
+
if (password === '') {
|
|
38
|
+
_error = 'Please type in your new password.';
|
|
39
|
+
setFormData((prevFormData) => ({
|
|
40
|
+
...prevFormData,
|
|
41
|
+
passwordClass: 'inputError',
|
|
42
|
+
}));
|
|
43
|
+
} else {
|
|
44
|
+
if (password !== passwordRepeat) {
|
|
45
|
+
_error =
|
|
46
|
+
'The password does not match, please re-type the password.';
|
|
47
|
+
setFormData((prevFormData) => ({
|
|
48
|
+
...prevFormData,
|
|
49
|
+
passwordClass: 'inputError',
|
|
50
|
+
passwordRepeatClass: 'inputError',
|
|
51
|
+
}));
|
|
52
|
+
} else {
|
|
53
|
+
if (!validator.isStrongPassword(password)) {
|
|
54
|
+
_error =
|
|
55
|
+
'Your password is too weak it must contain the following:<br />- Must contain at least 8 characters.<br />- Must contain at least one uppercase letter.<br />- Must contain at least one lowercase letter.<br />- Must contain at least one number.<br />- Must contain at least one symbol.<br />';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return _error;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const handleSubmit = (e) => {
|
|
64
|
+
if (e) {
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
const errorMsg = validatePassword();
|
|
67
|
+
|
|
68
|
+
if (errorMsg === '') {
|
|
69
|
+
CustomFetch(
|
|
70
|
+
'/password/reset',
|
|
71
|
+
'POST',
|
|
72
|
+
{
|
|
73
|
+
code: code,
|
|
74
|
+
password: password,
|
|
75
|
+
password_confirmation: passwordRepeat,
|
|
76
|
+
},
|
|
77
|
+
function (result) {
|
|
78
|
+
if (result.error === '') {
|
|
79
|
+
toast.success(
|
|
80
|
+
'You have successfully set a new password, please use your new password to login into the system.'
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
navigate('/login');
|
|
84
|
+
} else {
|
|
85
|
+
toast.error(result.error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
} else {
|
|
90
|
+
toast.error(<div>{parse(errorMsg)}</div>);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<div className="lcontainer">
|
|
97
|
+
<div className="lwrap">
|
|
98
|
+
<aside className="aside"></aside>
|
|
99
|
+
<div className="logincontainer">
|
|
100
|
+
<form onSubmit={handleSubmit}>
|
|
101
|
+
<Reveal effect="fadeInUp">
|
|
102
|
+
<div className="login">
|
|
103
|
+
<div className="formItem fwItem">
|
|
104
|
+
<h1>Reset your password</h1>
|
|
105
|
+
</div>
|
|
106
|
+
{errorMsg === '' ? (
|
|
107
|
+
<>
|
|
108
|
+
<div className="formItem fwItem">
|
|
109
|
+
<label className="fi__label">
|
|
110
|
+
<input
|
|
111
|
+
type="password"
|
|
112
|
+
name="password"
|
|
113
|
+
value={password}
|
|
114
|
+
onChange={handleInputChange}
|
|
115
|
+
tabIndex="1"
|
|
116
|
+
className={passwordClass}
|
|
117
|
+
/>
|
|
118
|
+
<span className="fi__span">
|
|
119
|
+
Password
|
|
120
|
+
</span>
|
|
121
|
+
</label>
|
|
122
|
+
</div>
|
|
123
|
+
<div className="formItem fwItem">
|
|
124
|
+
<label className="fi__label">
|
|
125
|
+
<input
|
|
126
|
+
type="password"
|
|
127
|
+
name="passwordRepeat"
|
|
128
|
+
value={passwordRepeat}
|
|
129
|
+
onChange={handleInputChange}
|
|
130
|
+
tabIndex="2"
|
|
131
|
+
className={
|
|
132
|
+
passwordRepeatClass
|
|
133
|
+
}
|
|
134
|
+
/>
|
|
135
|
+
<span className="fi__span">
|
|
136
|
+
Re-type Password
|
|
137
|
+
</span>
|
|
138
|
+
</label>
|
|
139
|
+
</div>
|
|
140
|
+
<div className="formItem fwItem lastItem">
|
|
141
|
+
<button
|
|
142
|
+
className="btn"
|
|
143
|
+
type="submit"
|
|
144
|
+
tabIndex="3"
|
|
145
|
+
>
|
|
146
|
+
Reset
|
|
147
|
+
</button>
|
|
148
|
+
</div>
|
|
149
|
+
</>
|
|
150
|
+
) : (
|
|
151
|
+
<p>{errorMsg}</p>
|
|
152
|
+
)}
|
|
153
|
+
</div>
|
|
154
|
+
</Reveal>
|
|
155
|
+
</form>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export default Verify;
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { useParams, Link } from 'react-router-dom';
|
|
3
|
+
import moment from 'moment';
|
|
4
|
+
import parse from 'html-react-parser';
|
|
5
|
+
import Popup from 'reactjs-popup';
|
|
6
|
+
import { CircleChevronRight } from 'akar-icons';
|
|
7
|
+
|
|
8
|
+
import CustomFetch from '../Fetch';
|
|
9
|
+
import Form from '../Form';
|
|
10
|
+
import Table from '../DataGrid';
|
|
11
|
+
import TableFilter from '../TableFilter';
|
|
12
|
+
|
|
13
|
+
function GenericDetail({ setting, urlParam, userProfile }) {
|
|
14
|
+
const routeParams = useParams();
|
|
15
|
+
const { filters, page, tabs } = setting;
|
|
16
|
+
|
|
17
|
+
/** General States */
|
|
18
|
+
const [config, setConfig] = useState({});
|
|
19
|
+
const [data, setData] = useState({});
|
|
20
|
+
const [dataReload, setDataReload] = useState(0);
|
|
21
|
+
const [subnav, setSubnav] = useState(filters || []);
|
|
22
|
+
const [total, setTotal] = useState(0);
|
|
23
|
+
|
|
24
|
+
/** General Functions */
|
|
25
|
+
const handleReload = () => {
|
|
26
|
+
const min = 0;
|
|
27
|
+
const max = 999999999;
|
|
28
|
+
const randomNumber = Math.floor(Math.random() * (max - min + 1) + min);
|
|
29
|
+
|
|
30
|
+
setDataReload(randomNumber);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getValueFromData = (data, path, id) => {
|
|
34
|
+
let value =
|
|
35
|
+
path.reduce(
|
|
36
|
+
(result, key) =>
|
|
37
|
+
result
|
|
38
|
+
? Array.isArray(result[key])
|
|
39
|
+
? result[key][0]
|
|
40
|
+
: result[key]
|
|
41
|
+
: '',
|
|
42
|
+
data
|
|
43
|
+
) ?? '';
|
|
44
|
+
|
|
45
|
+
if (value) {
|
|
46
|
+
if (Array.isArray(id)) {
|
|
47
|
+
return id.map((key) => value[key]).join(' ');
|
|
48
|
+
} else {
|
|
49
|
+
return value[id];
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const formatDate = (date) => {
|
|
57
|
+
return date && moment(date).format('DD-MM-YYYY') !== '01-01-1970'
|
|
58
|
+
? moment(date).format('DD-MM-YYYY')
|
|
59
|
+
: '';
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const renderValue = (item, data) => {
|
|
63
|
+
const { type, id, relation, size } = item;
|
|
64
|
+
const value =
|
|
65
|
+
relation && relation.length > 0
|
|
66
|
+
? getValueFromData(data, [...relation], id)
|
|
67
|
+
: getValueFromData(data, [], id);
|
|
68
|
+
|
|
69
|
+
const stringValue = String(value ? value : ''); // Convert value to a string
|
|
70
|
+
|
|
71
|
+
switch (type) {
|
|
72
|
+
case 'date':
|
|
73
|
+
return formatDate(value);
|
|
74
|
+
case 'exist':
|
|
75
|
+
return value ? 'Yes' : 'No';
|
|
76
|
+
case 'latest_notes':
|
|
77
|
+
const noteData = data?.[id]?.[0];
|
|
78
|
+
const name = noteData?.user?.name ?? '';
|
|
79
|
+
const description = noteData?.description ?? '';
|
|
80
|
+
const created_at = noteData?.created_at ?? '';
|
|
81
|
+
const formattedDate = formatDate(created_at);
|
|
82
|
+
|
|
83
|
+
if (name !== '' && description !== '' && formattedDate !== '') {
|
|
84
|
+
return parse(
|
|
85
|
+
`<br /><p>${name} - ${formattedDate}</p><p>${description}</p>`
|
|
86
|
+
);
|
|
87
|
+
} else {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
default:
|
|
91
|
+
return size === 'full' && stringValue
|
|
92
|
+
? parse(`<br /><p>${stringValue}</p>`)
|
|
93
|
+
: stringValue;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const renderContent = () => {
|
|
98
|
+
if (subnav && subnav.length > 0) {
|
|
99
|
+
const activeTab = subnav.find((s) => s.show === true);
|
|
100
|
+
|
|
101
|
+
if (activeTab) {
|
|
102
|
+
const activeTabConfig = tabs.find((t) => t.id === activeTab.id);
|
|
103
|
+
|
|
104
|
+
if (activeTabConfig && activeTabConfig.type) {
|
|
105
|
+
switch (activeTabConfig.type) {
|
|
106
|
+
case 'form':
|
|
107
|
+
return (
|
|
108
|
+
<div className="gridtxt">
|
|
109
|
+
<div className="gridtxt__header">
|
|
110
|
+
<span>{activeTabConfig.title}</span>
|
|
111
|
+
</div>
|
|
112
|
+
<Form
|
|
113
|
+
columnId={routeParams[urlParam]}
|
|
114
|
+
formSettings={activeTabConfig.form}
|
|
115
|
+
formType="update"
|
|
116
|
+
style={
|
|
117
|
+
userProfile?.settings?.style || {}
|
|
118
|
+
}
|
|
119
|
+
updateForm={setFormData}
|
|
120
|
+
type="inline"
|
|
121
|
+
/>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
case 'overview':
|
|
125
|
+
return (
|
|
126
|
+
<>
|
|
127
|
+
<div className="gridactions">
|
|
128
|
+
<ul>
|
|
129
|
+
<li>
|
|
130
|
+
<button
|
|
131
|
+
className="btn"
|
|
132
|
+
onClick={() =>
|
|
133
|
+
modalOpen(
|
|
134
|
+
'update',
|
|
135
|
+
routeParams[
|
|
136
|
+
urlParam
|
|
137
|
+
]
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
>
|
|
141
|
+
Edit
|
|
142
|
+
</button>
|
|
143
|
+
</li>
|
|
144
|
+
</ul>
|
|
145
|
+
</div>
|
|
146
|
+
<div className="gridtxt">
|
|
147
|
+
{activeTabConfig.sections.map(
|
|
148
|
+
(section, sectionIndex) => (
|
|
149
|
+
<React.Fragment
|
|
150
|
+
key={`section-${activeTabConfig.id}-${sectionIndex}`}
|
|
151
|
+
>
|
|
152
|
+
<div className="gridtxt__header">
|
|
153
|
+
<span>
|
|
154
|
+
{section.title}
|
|
155
|
+
</span>
|
|
156
|
+
</div>
|
|
157
|
+
{section.content &&
|
|
158
|
+
section.content.length >
|
|
159
|
+
0 && (
|
|
160
|
+
<ul className="customer__overview">
|
|
161
|
+
{section.content.map(
|
|
162
|
+
(
|
|
163
|
+
item,
|
|
164
|
+
itemKey
|
|
165
|
+
) => (
|
|
166
|
+
<li
|
|
167
|
+
key={`li-${item.id}-${itemKey}`}
|
|
168
|
+
className={
|
|
169
|
+
item.size ===
|
|
170
|
+
'full'
|
|
171
|
+
? 'fw-grid-item notecolor'
|
|
172
|
+
: null
|
|
173
|
+
}
|
|
174
|
+
>
|
|
175
|
+
<strong>
|
|
176
|
+
{
|
|
177
|
+
item.label
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
:
|
|
181
|
+
</strong>{' '}
|
|
182
|
+
{renderValue(
|
|
183
|
+
item,
|
|
184
|
+
data
|
|
185
|
+
)}
|
|
186
|
+
</li>
|
|
187
|
+
)
|
|
188
|
+
)}
|
|
189
|
+
</ul>
|
|
190
|
+
)}
|
|
191
|
+
</React.Fragment>
|
|
192
|
+
)
|
|
193
|
+
)}
|
|
194
|
+
</div>
|
|
195
|
+
</>
|
|
196
|
+
);
|
|
197
|
+
case 'table':
|
|
198
|
+
if (
|
|
199
|
+
config.ajaxSetting &&
|
|
200
|
+
config.form &&
|
|
201
|
+
config.columns &&
|
|
202
|
+
config.settings
|
|
203
|
+
) {
|
|
204
|
+
return (
|
|
205
|
+
<Table
|
|
206
|
+
{...config}
|
|
207
|
+
setConfig={setConfig}
|
|
208
|
+
setTotal={setTotal}
|
|
209
|
+
style={
|
|
210
|
+
userProfile?.settings?.style || {}
|
|
211
|
+
}
|
|
212
|
+
/>
|
|
213
|
+
);
|
|
214
|
+
} else {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
default:
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/** General Hooks */
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
if (tabs && tabs.length > 0) {
|
|
228
|
+
const activeTab = subnav.find((s) => s.show === true);
|
|
229
|
+
|
|
230
|
+
if (activeTab) {
|
|
231
|
+
const activeTabConfig = tabs.find((t) => t.id === activeTab.id);
|
|
232
|
+
|
|
233
|
+
const applyUrlParamsToData = (data) => {
|
|
234
|
+
return data.map((item) => {
|
|
235
|
+
return {
|
|
236
|
+
...item,
|
|
237
|
+
value:
|
|
238
|
+
item.hasOwnProperty('value') &&
|
|
239
|
+
item.urlParam &&
|
|
240
|
+
routeParams[item.urlParam]
|
|
241
|
+
? routeParams[item.urlParam]
|
|
242
|
+
: item.value,
|
|
243
|
+
};
|
|
244
|
+
});
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
if (activeTabConfig) {
|
|
248
|
+
switch (activeTabConfig.type) {
|
|
249
|
+
case 'table':
|
|
250
|
+
if (
|
|
251
|
+
activeTabConfig.ajaxSetting &&
|
|
252
|
+
activeTabConfig.ajaxSetting.where &&
|
|
253
|
+
activeTabConfig.ajaxSetting.where.length > 0
|
|
254
|
+
) {
|
|
255
|
+
const updatedAjaxSetting = {
|
|
256
|
+
...activeTabConfig.ajaxSetting,
|
|
257
|
+
where: applyUrlParamsToData(
|
|
258
|
+
activeTabConfig.ajaxSetting.where
|
|
259
|
+
),
|
|
260
|
+
};
|
|
261
|
+
const updatedForm = {
|
|
262
|
+
...activeTabConfig.form,
|
|
263
|
+
fields: applyUrlParamsToData(
|
|
264
|
+
activeTabConfig.form.fields
|
|
265
|
+
),
|
|
266
|
+
};
|
|
267
|
+
setConfig({
|
|
268
|
+
ajaxSetting: updatedAjaxSetting,
|
|
269
|
+
form: updatedForm,
|
|
270
|
+
columns: activeTabConfig.columns,
|
|
271
|
+
settings: activeTabConfig.settings,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
break;
|
|
275
|
+
case 'overview':
|
|
276
|
+
setConfig({});
|
|
277
|
+
if (activeTabConfig.form) {
|
|
278
|
+
setFormData(activeTabConfig.form);
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
default:
|
|
282
|
+
setConfig({});
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}, [subnav, tabs, routeParams]);
|
|
289
|
+
|
|
290
|
+
useEffect(() => {
|
|
291
|
+
if (routeParams[urlParam] && routeParams[urlParam] > 0) {
|
|
292
|
+
CustomFetch(
|
|
293
|
+
`${page.fetchUrl}/${routeParams[urlParam]}`,
|
|
294
|
+
'GET',
|
|
295
|
+
{},
|
|
296
|
+
(res) => {
|
|
297
|
+
setData(res);
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}, [routeParams, dataReload]);
|
|
302
|
+
|
|
303
|
+
/** Modal States */
|
|
304
|
+
const [formData, setFormData] = useState({});
|
|
305
|
+
const [formType, setFormType] = useState('');
|
|
306
|
+
const [formId, setFormId] = useState(0);
|
|
307
|
+
const [modalShow, setModalShow] = useState(false);
|
|
308
|
+
|
|
309
|
+
/** Modal Functions */
|
|
310
|
+
const modalOpen = (formType, formId) => {
|
|
311
|
+
setModalShow(true);
|
|
312
|
+
setFormType(formType);
|
|
313
|
+
setFormId(formId);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const modalClose = () => {
|
|
317
|
+
setModalShow(false);
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
return (
|
|
321
|
+
<>
|
|
322
|
+
<div className="grid">
|
|
323
|
+
<div className="grid__row">
|
|
324
|
+
<div className="grid__full crmtitle">
|
|
325
|
+
<h1>
|
|
326
|
+
<Link
|
|
327
|
+
to={
|
|
328
|
+
page && page.previousUrl
|
|
329
|
+
? page.previousUrl
|
|
330
|
+
: null
|
|
331
|
+
}
|
|
332
|
+
>
|
|
333
|
+
{page && page.title ? page.title : null}
|
|
334
|
+
</Link>{' '}
|
|
335
|
+
<CircleChevronRight strokeWidth={2} size={18} />{' '}
|
|
336
|
+
{data && data[page.titleKey]
|
|
337
|
+
? data[page.titleKey]
|
|
338
|
+
: null}
|
|
339
|
+
</h1>
|
|
340
|
+
{total > 0 && (
|
|
341
|
+
<div className="titleInfo">
|
|
342
|
+
<span>
|
|
343
|
+
[<strong>{total}</strong> Total]
|
|
344
|
+
</span>
|
|
345
|
+
</div>
|
|
346
|
+
)}
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
<div className="grid">
|
|
351
|
+
<div className="grid__subrow">
|
|
352
|
+
<div className="grid__subnav">
|
|
353
|
+
<TableFilter filters={subnav} setFilters={setSubnav} />
|
|
354
|
+
</div>
|
|
355
|
+
<div className="grid__subcontent">
|
|
356
|
+
{config ? renderContent() : null}
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
<Popup
|
|
362
|
+
open={modalShow}
|
|
363
|
+
onClose={modalClose}
|
|
364
|
+
closeOnDocumentClick={false}
|
|
365
|
+
>
|
|
366
|
+
<Form
|
|
367
|
+
closeModal={modalClose}
|
|
368
|
+
columnId={formId}
|
|
369
|
+
fetchTable={handleReload}
|
|
370
|
+
formSettings={formData}
|
|
371
|
+
formType={formType}
|
|
372
|
+
style={userProfile?.settings?.style || {}}
|
|
373
|
+
updateForm={setFormData}
|
|
374
|
+
/>
|
|
375
|
+
</Popup>
|
|
376
|
+
</>
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export default GenericDetail;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Outlet } from 'react-router-dom';
|
|
3
|
+
import Table from '../DataGrid';
|
|
4
|
+
import TableFilter from '../TableFilter';
|
|
5
|
+
|
|
6
|
+
function GenericIndex({ setting, userProfile }) {
|
|
7
|
+
const { filters, page, tabs, ajaxSetting, form, columns, settings } =
|
|
8
|
+
setting;
|
|
9
|
+
|
|
10
|
+
const [config, setConfig] = useState({});
|
|
11
|
+
const [subnav, setSubnav] = useState(filters || []);
|
|
12
|
+
const [total, setTotal] = useState(0);
|
|
13
|
+
const pageTitle = page?.title;
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!tabs || tabs.length === 0) {
|
|
17
|
+
setConfig(() => ({
|
|
18
|
+
ajaxSetting,
|
|
19
|
+
form,
|
|
20
|
+
columns,
|
|
21
|
+
settings,
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setSubnav(filters);
|
|
26
|
+
}, [setting]);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (tabs && tabs.length > 0) {
|
|
30
|
+
const activeTab = subnav.find((s) => s.show === true);
|
|
31
|
+
|
|
32
|
+
if (activeTab) {
|
|
33
|
+
const activeTabConfig = tabs.find((t) => t.id === activeTab.id);
|
|
34
|
+
|
|
35
|
+
if (activeTabConfig) {
|
|
36
|
+
setConfig(() => ({
|
|
37
|
+
ajaxSetting: activeTabConfig.ajaxSetting,
|
|
38
|
+
form: activeTabConfig.form,
|
|
39
|
+
columns: activeTabConfig.columns,
|
|
40
|
+
settings: activeTabConfig.settings,
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}, [subnav, tabs]);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<>
|
|
49
|
+
<div className="grid">
|
|
50
|
+
<div className="grid__row">
|
|
51
|
+
<div className="grid__full crmtitle">
|
|
52
|
+
<h1>{pageTitle}</h1>
|
|
53
|
+
<div className="titleInfo">
|
|
54
|
+
<span>
|
|
55
|
+
[<strong>{total}</strong> Total {pageTitle}]
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
{config.ajaxSetting &&
|
|
63
|
+
config.form &&
|
|
64
|
+
config.columns &&
|
|
65
|
+
config.settings && (
|
|
66
|
+
<>
|
|
67
|
+
<div className="grid">
|
|
68
|
+
<div className="grid__row">
|
|
69
|
+
{subnav && subnav.length > 0 ? (
|
|
70
|
+
<>
|
|
71
|
+
<div className="grid__subnav">
|
|
72
|
+
<TableFilter
|
|
73
|
+
filters={subnav}
|
|
74
|
+
setFilters={setSubnav}
|
|
75
|
+
setSettings={setConfig}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
<div className="grid__subcontent">
|
|
79
|
+
<Table
|
|
80
|
+
{...config}
|
|
81
|
+
setConfig={setConfig}
|
|
82
|
+
setTotal={setTotal}
|
|
83
|
+
style={
|
|
84
|
+
userProfile?.settings
|
|
85
|
+
?.style || {}
|
|
86
|
+
}
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
</>
|
|
90
|
+
) : (
|
|
91
|
+
<div className="grid__full">
|
|
92
|
+
<Table
|
|
93
|
+
{...config}
|
|
94
|
+
setConfig={setConfig}
|
|
95
|
+
setTotal={setTotal}
|
|
96
|
+
style={
|
|
97
|
+
userProfile?.settings?.style ||
|
|
98
|
+
{}
|
|
99
|
+
}
|
|
100
|
+
/>
|
|
101
|
+
</div>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
<Outlet />
|
|
106
|
+
</>
|
|
107
|
+
)}
|
|
108
|
+
</>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export default GenericIndex;
|