@visns-studio/visns-components 1.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/components/visns-async-select.jsx +75 -0
- package/components/visns-autocomplete.jsx +147 -0
- package/components/visns-call.jsx +220 -0
- package/components/visns-data-grid.jsx +598 -0
- package/components/visns-download.jsx +102 -0
- package/components/visns-fetch.jsx +101 -0
- package/components/visns-field.jsx +662 -0
- package/components/visns-form.jsx +1058 -0
- package/components/visns-loader.jsx +28 -0
- package/components/visns-multi-select.jsx +76 -0
- package/components/visns-notification.jsx +202 -0
- package/components/visns-select-list.jsx +21 -0
- package/components/visns-select.jsx +29 -0
- package/components/visns-table-filter.jsx +124 -0
- package/index.js +12 -0
- package/package.json +44 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React, { useEffect, useState, useCallback } from 'react';
|
|
2
|
+
import CustomFetch from './visns-fetch';
|
|
3
|
+
import SelectList from './visns-select-list';
|
|
4
|
+
import AsyncSelect from 'react-select/async';
|
|
5
|
+
import { debounce } from 'lodash';
|
|
6
|
+
|
|
7
|
+
function VisnsAsyncSelect(props) {
|
|
8
|
+
const { inputValue, onChange, settings, multi } = props;
|
|
9
|
+
|
|
10
|
+
const loadSuggestedOptions = useCallback(
|
|
11
|
+
debounce((inputValue, callback) => {
|
|
12
|
+
if (inputValue !== '') {
|
|
13
|
+
loadOptions(inputValue).then((options) => callback(options));
|
|
14
|
+
}
|
|
15
|
+
}, 500),
|
|
16
|
+
[]
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const loadOptions = (inputValue) => {
|
|
20
|
+
if (inputValue !== '') {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
let data = [];
|
|
23
|
+
|
|
24
|
+
CustomFetch(
|
|
25
|
+
settings.url,
|
|
26
|
+
'POST',
|
|
27
|
+
{
|
|
28
|
+
where: [
|
|
29
|
+
{
|
|
30
|
+
id: 'async',
|
|
31
|
+
value: inputValue,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
function (result) {
|
|
36
|
+
result.data.forEach((a) => {
|
|
37
|
+
data.push({
|
|
38
|
+
value: a.id,
|
|
39
|
+
label: a.label,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
resolve(data);
|
|
44
|
+
},
|
|
45
|
+
function (error) {
|
|
46
|
+
reject(String(error));
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<AsyncSelect
|
|
55
|
+
isClearable
|
|
56
|
+
cacheOptions
|
|
57
|
+
loadOptions={loadSuggestedOptions}
|
|
58
|
+
defaultOptions
|
|
59
|
+
className="visns__ms"
|
|
60
|
+
onChange={(inputValue, action) => {
|
|
61
|
+
onChange(inputValue, action, settings.id);
|
|
62
|
+
}}
|
|
63
|
+
components={{ SelectList }}
|
|
64
|
+
styles={{
|
|
65
|
+
menu: (provided) => ({
|
|
66
|
+
...provided,
|
|
67
|
+
zIndex: 9999,
|
|
68
|
+
}),
|
|
69
|
+
}}
|
|
70
|
+
value={inputValue}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default VisnsAsyncSelect;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import AwesomeDebouncePromise from 'awesome-debounce-promise';
|
|
4
|
+
import { ToggleOffFill, ToggleOnFill } from 'akar-icons';
|
|
5
|
+
|
|
6
|
+
const VisnsAutocomplete = (props) => {
|
|
7
|
+
const { api, country, onSelect, setFormData, field, inputValue } = props;
|
|
8
|
+
const [search, setSearch] = useState('');
|
|
9
|
+
const [results, setResults] = useState([]);
|
|
10
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
11
|
+
const [overwrite, setOverwrite] = useState(false);
|
|
12
|
+
|
|
13
|
+
const performSearch = (id, value) => {
|
|
14
|
+
if (id > 0) {
|
|
15
|
+
if (value === '') {
|
|
16
|
+
setResults([]);
|
|
17
|
+
setIsLoading(false);
|
|
18
|
+
} else {
|
|
19
|
+
let url = `https://api.mapbox.com/geocoding/v5/mapbox.places/${value}.json?types=address,poi&access_token=${api}`;
|
|
20
|
+
|
|
21
|
+
if (country && country !== '') {
|
|
22
|
+
url += `&country=${country}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (import.meta.env.hasOwnProperty('VITE_MAPBOX_PROXIMITY')) {
|
|
26
|
+
url += `&proximity=${
|
|
27
|
+
import.meta.env.VITE_MAPBOX_PROXIMITY
|
|
28
|
+
}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
axios
|
|
32
|
+
.get(url)
|
|
33
|
+
.then((response) => {
|
|
34
|
+
setResults(response.data.features);
|
|
35
|
+
setIsLoading(false);
|
|
36
|
+
})
|
|
37
|
+
.catch(function (error) {
|
|
38
|
+
console.log(error);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleSearchChange = async (e) => {
|
|
45
|
+
if (e) {
|
|
46
|
+
setSearch(e.target.value);
|
|
47
|
+
setFormData((prevState) => ({
|
|
48
|
+
...prevState,
|
|
49
|
+
[field]: e.target.value,
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
if (overwrite === false) {
|
|
53
|
+
setIsLoading(true);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (overwrite === false) {
|
|
57
|
+
await performSearchDebounced(1, e.target.value);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const performSearchDebounced = AwesomeDebouncePromise(performSearch, 500, {
|
|
63
|
+
key: (id, value) => id,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const handleItemClicked = (place) => {
|
|
67
|
+
setSearch(place.address + ' ' + place.text);
|
|
68
|
+
setResults([]);
|
|
69
|
+
onSelect(place, field);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handleOverwrite = () => {
|
|
73
|
+
if (overwrite === false) {
|
|
74
|
+
setOverwrite(true);
|
|
75
|
+
setResults([]);
|
|
76
|
+
} else {
|
|
77
|
+
setOverwrite(false);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (overwrite === false) {
|
|
83
|
+
if (search !== '') {
|
|
84
|
+
performSearch();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}, [overwrite]);
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
setSearch(inputValue);
|
|
91
|
+
}, [inputValue]);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<>
|
|
95
|
+
<div className="AutocompletePlace">
|
|
96
|
+
<input
|
|
97
|
+
className="AutocompletePlace-input"
|
|
98
|
+
style={{ height: '53px' }}
|
|
99
|
+
type="text"
|
|
100
|
+
value={search || ''}
|
|
101
|
+
onChange={handleSearchChange}
|
|
102
|
+
placeholder="Type an address"
|
|
103
|
+
autoComplete="off"
|
|
104
|
+
/>
|
|
105
|
+
{overwrite === false ? (
|
|
106
|
+
<ToggleOffFill
|
|
107
|
+
data-tooltip-id="system-tooltip"
|
|
108
|
+
data-tooltip-content="Disable Autocomplete"
|
|
109
|
+
strokeWidth={2}
|
|
110
|
+
size={24}
|
|
111
|
+
onClick={handleOverwrite}
|
|
112
|
+
/>
|
|
113
|
+
) : (
|
|
114
|
+
<ToggleOnFill
|
|
115
|
+
data-tooltip-id="system-tooltip"
|
|
116
|
+
data-tooltip-content="Enable Autocomplete"
|
|
117
|
+
strokeWidth={2}
|
|
118
|
+
size={24}
|
|
119
|
+
onClick={handleOverwrite}
|
|
120
|
+
className="toggleActive"
|
|
121
|
+
/>
|
|
122
|
+
)}
|
|
123
|
+
{results.length > 0 ? (
|
|
124
|
+
<ul className="AutocompletePlace-results">
|
|
125
|
+
{results.map((place) => (
|
|
126
|
+
<li
|
|
127
|
+
key={place.id}
|
|
128
|
+
className="AutocompletePlace-items"
|
|
129
|
+
onClick={() => handleItemClicked(place)}
|
|
130
|
+
>
|
|
131
|
+
{place.place_name}
|
|
132
|
+
</li>
|
|
133
|
+
))}
|
|
134
|
+
|
|
135
|
+
{isLoading && (
|
|
136
|
+
<li className="AutocompletePlace-items">
|
|
137
|
+
Loading...
|
|
138
|
+
</li>
|
|
139
|
+
)}
|
|
140
|
+
</ul>
|
|
141
|
+
) : null}
|
|
142
|
+
</div>
|
|
143
|
+
</>
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export default VisnsAutocomplete;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
|
+
import Popup from "reactjs-popup";
|
|
3
|
+
import moment from "moment";
|
|
4
|
+
|
|
5
|
+
import CustomFetch from "./visns-fetch";
|
|
6
|
+
|
|
7
|
+
function CallPop(props) {
|
|
8
|
+
const { incomingCallData } = props;
|
|
9
|
+
const [modalShow, setModalShow] = useState(false);
|
|
10
|
+
const [client, setClient] = useState({});
|
|
11
|
+
const [tasks, setTasks] = useState([]);
|
|
12
|
+
const [callShow, setCallShow] = useState(false);
|
|
13
|
+
const [timerId, setTimerId] = useState(0);
|
|
14
|
+
|
|
15
|
+
const formatPhone = (value) => {
|
|
16
|
+
if (value) {
|
|
17
|
+
let _value = value.replace("61", 0);
|
|
18
|
+
let match;
|
|
19
|
+
let type = _value.substr(0, 2);
|
|
20
|
+
|
|
21
|
+
if (_value && _value !== "") {
|
|
22
|
+
switch (type) {
|
|
23
|
+
case "04":
|
|
24
|
+
match = _value.match(/^(\d{4})(\d{3})(\d{3})$/);
|
|
25
|
+
if (match) {
|
|
26
|
+
_value = match[1] + " " + match[2] + " " + match[3];
|
|
27
|
+
}
|
|
28
|
+
break;
|
|
29
|
+
default:
|
|
30
|
+
match = _value.match(/^(\d{2})(\d{4})(\d{4})$/);
|
|
31
|
+
if (match) {
|
|
32
|
+
_value = match[1] + " " + match[2] + " " + match[3];
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return _value;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const viewModal = () => {
|
|
43
|
+
setModalShow(true);
|
|
44
|
+
|
|
45
|
+
if (timerId > 0) {
|
|
46
|
+
clearTimeout(timerId);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (
|
|
52
|
+
incomingCallData &&
|
|
53
|
+
incomingCallData.number &&
|
|
54
|
+
incomingCallData.number != ""
|
|
55
|
+
) {
|
|
56
|
+
CustomFetch(
|
|
57
|
+
"/incoming/check",
|
|
58
|
+
"POST",
|
|
59
|
+
{
|
|
60
|
+
user_id: incomingCallData.user_id,
|
|
61
|
+
},
|
|
62
|
+
function (result) {
|
|
63
|
+
if (result.show === true) {
|
|
64
|
+
setCallShow(true);
|
|
65
|
+
|
|
66
|
+
let _timerId = setTimeout(() => {
|
|
67
|
+
setCallShow(false);
|
|
68
|
+
}, 20000);
|
|
69
|
+
|
|
70
|
+
setTimerId(_timerId);
|
|
71
|
+
|
|
72
|
+
CustomFetch(
|
|
73
|
+
"/company/call",
|
|
74
|
+
"POST",
|
|
75
|
+
{
|
|
76
|
+
search: incomingCallData.number,
|
|
77
|
+
},
|
|
78
|
+
function (result) {
|
|
79
|
+
setClient(result);
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
} else {
|
|
83
|
+
setCallShow(false);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}, [incomingCallData]);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<>
|
|
92
|
+
{callShow === true ? (
|
|
93
|
+
<>
|
|
94
|
+
<div className="callpop callpop--alive">
|
|
95
|
+
<div className="callpop__left">
|
|
96
|
+
<span>
|
|
97
|
+
<strong>Active Call:</strong>{" "}
|
|
98
|
+
{formatPhone(incomingCallData.number)}{" "}
|
|
99
|
+
<strong>Client:</strong>{" "}
|
|
100
|
+
{setData
|
|
101
|
+
? setData.firstname === ""
|
|
102
|
+
? "No contacts found with the incoming number."
|
|
103
|
+
: setData.firstname +
|
|
104
|
+
" " +
|
|
105
|
+
setData.surname +
|
|
106
|
+
" (" +
|
|
107
|
+
setData.customer.name +
|
|
108
|
+
")"
|
|
109
|
+
: "No contacts found with the incoming number."}
|
|
110
|
+
</span>
|
|
111
|
+
</div>
|
|
112
|
+
<div className="callpop__right">
|
|
113
|
+
<button className="btn cpview" onClick={viewModal}>
|
|
114
|
+
View{" "}
|
|
115
|
+
<i className="cil-chevron-circle-up-alt "></i>
|
|
116
|
+
</button>
|
|
117
|
+
<button
|
|
118
|
+
className="btn cpdismiss"
|
|
119
|
+
onClick={() => {
|
|
120
|
+
setCallShow(false);
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
Dismiss
|
|
124
|
+
</button>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
<Popup
|
|
128
|
+
open={modalShow}
|
|
129
|
+
onClose={() => {
|
|
130
|
+
setCallShow(false);
|
|
131
|
+
setModalShow(false);
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
<div className="modalwrap">
|
|
135
|
+
<div className="grid">
|
|
136
|
+
<div className="grid__row">
|
|
137
|
+
<div className="grid__full crmtitle callborder">
|
|
138
|
+
<i className="cil-phone-in-talk"></i>
|
|
139
|
+
<h1>
|
|
140
|
+
Active Call:{" "}
|
|
141
|
+
<span>
|
|
142
|
+
{formatPhone(
|
|
143
|
+
incomingCallData.number
|
|
144
|
+
)}
|
|
145
|
+
</span>{" "}
|
|
146
|
+
Client:{" "}
|
|
147
|
+
<span>
|
|
148
|
+
{setData
|
|
149
|
+
? setData.firstname === ""
|
|
150
|
+
? "No contacts found with the incoming number."
|
|
151
|
+
: setData.firstname +
|
|
152
|
+
" " +
|
|
153
|
+
setData.surname +
|
|
154
|
+
" (" +
|
|
155
|
+
setData.customer
|
|
156
|
+
.name +
|
|
157
|
+
")"
|
|
158
|
+
: "No contacts found with the incoming number."}
|
|
159
|
+
</span>{" "}
|
|
160
|
+
</h1>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
<div className="grid">
|
|
165
|
+
<div className="grid__row">
|
|
166
|
+
<div className="grid__full">
|
|
167
|
+
<table className="content-table">
|
|
168
|
+
<thead>
|
|
169
|
+
<tr className="grid-headers">
|
|
170
|
+
<th>Ticket #</th>
|
|
171
|
+
<th>Subject</th>
|
|
172
|
+
<th>Status</th>
|
|
173
|
+
<th>Created</th>
|
|
174
|
+
</tr>
|
|
175
|
+
</thead>
|
|
176
|
+
<tbody>
|
|
177
|
+
{data &&
|
|
178
|
+
data.tickets &&
|
|
179
|
+
data.tickets.length > 0 ? (
|
|
180
|
+
data.tickets.map((a) => (
|
|
181
|
+
<tr
|
|
182
|
+
key={`task-id-${a.id}`}
|
|
183
|
+
>
|
|
184
|
+
<td>{a.id}</td>
|
|
185
|
+
<td>{a.subject}</td>
|
|
186
|
+
<td>
|
|
187
|
+
{a.status.label}
|
|
188
|
+
</td>
|
|
189
|
+
<td>
|
|
190
|
+
{moment(
|
|
191
|
+
a.created_at
|
|
192
|
+
).format(
|
|
193
|
+
"DD-MM-YYYY"
|
|
194
|
+
)}
|
|
195
|
+
</td>
|
|
196
|
+
</tr>
|
|
197
|
+
))
|
|
198
|
+
) : (
|
|
199
|
+
<tr colSpan="10">
|
|
200
|
+
<td>
|
|
201
|
+
No tickets found
|
|
202
|
+
with the incoming
|
|
203
|
+
number.
|
|
204
|
+
</td>
|
|
205
|
+
</tr>
|
|
206
|
+
)}
|
|
207
|
+
</tbody>
|
|
208
|
+
</table>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</Popup>
|
|
214
|
+
</>
|
|
215
|
+
) : null}
|
|
216
|
+
</>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export default CallPop;
|