@modernman00/shared-js-lib 1.2.75 โ 1.2.77
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/AcctMgt/code.js +2 -1
- package/AcctMgt/forgot.js +1 -1
- package/AcctMgt/processAll.js +11 -3
- package/Cookie.js +1 -1
- package/DateTime.js +8 -4
- package/FileUpload.js +4 -1
- package/FormHelper.js +8 -3
- package/Http.js +16 -4
- package/Loader.js +6 -3
- package/ShowResponse.js +16 -10
- package/axiosWrapper.js +4 -3
- package/config/version.php +1 -1
- package/general.js +5 -1
- package/index.js +0 -1
- package/package.json +1 -1
- package/release-history.md +18 -0
package/AcctMgt/code.js
CHANGED
package/AcctMgt/forgot.js
CHANGED
package/AcctMgt/processAll.js
CHANGED
|
@@ -18,6 +18,11 @@ export const loginSubmission = async ({
|
|
|
18
18
|
optionalFields = [],
|
|
19
19
|
recaptchaToken = null // { token, action } or null
|
|
20
20
|
}) => {
|
|
21
|
+
let loaderClasses = "loader";
|
|
22
|
+
if (css === 'tailwind') {
|
|
23
|
+
loaderClasses = "animate-spin mx-auto block w-10 h-10 border-4 border-blue-600 border-t-transparent rounded-full";
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
try {
|
|
22
27
|
const formInput = id(formId);
|
|
23
28
|
|
|
@@ -37,14 +42,17 @@ export const loginSubmission = async ({
|
|
|
37
42
|
formData.massValidate(optionalFields, { email: 'email', password: 'password' });
|
|
38
43
|
|
|
39
44
|
if (formData.result !== 1) {
|
|
40
|
-
|
|
45
|
+
const firstError = document.querySelector('[id$="_error"]:not(:empty)');
|
|
46
|
+
if (firstError) {
|
|
47
|
+
firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
48
|
+
}
|
|
41
49
|
return;
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
const notificationElement = id(`${formId}_notification`) || formInput;
|
|
45
53
|
notificationElement.scrollIntoView({ behavior: 'smooth' });
|
|
46
54
|
|
|
47
|
-
showLoader();
|
|
55
|
+
showLoader('setLoader', loaderClasses);
|
|
48
56
|
localStorage.setItem('redirect', redirect);
|
|
49
57
|
|
|
50
58
|
await postFormData(loginURL, formId, redirect, css, recaptchaToken);
|
|
@@ -52,6 +60,6 @@ export const loginSubmission = async ({
|
|
|
52
60
|
} catch (err) {
|
|
53
61
|
showError(err);
|
|
54
62
|
} finally {
|
|
55
|
-
clearLoader();
|
|
63
|
+
clearLoader('setLoader', loaderClasses);
|
|
56
64
|
}
|
|
57
65
|
};
|
package/Cookie.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export const setCookie = (cname, cvalue, exdays = 365) => {
|
|
9
9
|
const d = new Date(Date.now() + exdays * 864e5);
|
|
10
|
-
document.cookie = `${cname}=${cvalue}; expires=${d.toUTCString()}; path
|
|
10
|
+
document.cookie = `${cname}=${cvalue}; expires=${d.toUTCString()}; path=/; SameSite=Lax`;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
package/DateTime.js
CHANGED
|
@@ -45,8 +45,10 @@ let element = document.querySelectorAll('time[data-time]')
|
|
|
45
45
|
*/
|
|
46
46
|
export const updateTimeRealTime = () => {
|
|
47
47
|
for (const entry of element) {
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
try {
|
|
49
|
+
const seconds = (Date.now() - new Date(entry.dataset.time).getTime()) / 1000;
|
|
50
|
+
entry.textContent = getDuration(seconds);
|
|
51
|
+
} catch (_) { /* skip malformed entry, continue updating others */ }
|
|
50
52
|
}
|
|
51
53
|
setTimeout(updateTimeRealTime, 1000 * 60);
|
|
52
54
|
};
|
|
@@ -93,9 +95,11 @@ const getDuration = (timeAgoInSeconds) => {
|
|
|
93
95
|
*/
|
|
94
96
|
export const timeAgo = (date) => {
|
|
95
97
|
const timeAgoInSeconds = Math.floor((new Date() - new Date(date)) / 1000);
|
|
96
|
-
const
|
|
98
|
+
const duration = getDuration(timeAgoInSeconds);
|
|
99
|
+
if (!duration) return '0 seconds ago';
|
|
100
|
+
const { interval, epoch } = duration;
|
|
97
101
|
const suffix = interval === 1 ? '' : 's';
|
|
98
102
|
return `${interval} ${epoch}${suffix} ago`;
|
|
99
103
|
};
|
|
100
104
|
|
|
101
|
-
export const date2String = (date) => new Date().toDateString(
|
|
105
|
+
export const date2String = (date) => new Date(date).toDateString()
|
package/FileUpload.js
CHANGED
|
@@ -9,8 +9,11 @@ import { id } from './UtilityHtml.js';
|
|
|
9
9
|
* @param {string} fileName - the id of the element where the selected file names will be displayed.
|
|
10
10
|
*/
|
|
11
11
|
export const showImageFileUploadFn = (uploadBtn, inputId, fileName) => {
|
|
12
|
+
const btn = id(uploadBtn);
|
|
12
13
|
const input = id(inputId);
|
|
13
14
|
const fileNamesEl = id(fileName);
|
|
14
|
-
|
|
15
|
+
if (!btn || !input || !fileNamesEl) return;
|
|
16
|
+
|
|
17
|
+
btn.addEventListener('click', () => input.click());
|
|
15
18
|
input.addEventListener('change', () => fileNamesEl.innerText = Array.from(input.files).map(file => file.name).join(', '));
|
|
16
19
|
};
|
package/FormHelper.js
CHANGED
|
@@ -114,10 +114,15 @@ export default class FormHelper {
|
|
|
114
114
|
!id ||
|
|
115
115
|
['submit', 'button', 'g-recaptcha-response', 'cancel'].includes(name) ||
|
|
116
116
|
['button', 'showPassword', 'token', 'g-recaptcha-response'].includes(id) ||
|
|
117
|
-
type === 'button' || optionalFields.includes(id)
|
|
118
|
-
id === 'checkbox'
|
|
117
|
+
type === 'button' || optionalFields.includes(id)
|
|
119
118
|
) continue;
|
|
120
119
|
|
|
120
|
+
// Skip hidden elements (if the user can't see it, they can't fill it)
|
|
121
|
+
const el = this.id(id);
|
|
122
|
+
if (el && (el.offsetParent === null || window.getComputedStyle(el).display === 'none')) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
121
126
|
|
|
122
127
|
|
|
123
128
|
|
|
@@ -131,7 +136,7 @@ export default class FormHelper {
|
|
|
131
136
|
}
|
|
132
137
|
|
|
133
138
|
// Required field check
|
|
134
|
-
if (val === '' || val === 'select') {
|
|
139
|
+
if (val === '' || val === 'select' || (type === 'checkbox' && !input.checked)) {
|
|
135
140
|
if (errorEl) errorEl.innerHTML = `<li style="color:red;">${id} cannot be left empty.</li>`;
|
|
136
141
|
this.error.push(`${id.toUpperCase()} cannot be left empty.`);
|
|
137
142
|
continue;
|
package/Http.js
CHANGED
|
@@ -96,6 +96,7 @@ export const postFormData = async (url, formId, redirect = null, css = null, rec
|
|
|
96
96
|
const processFormDataAction = (cssClass, message, formNotificationId) => {
|
|
97
97
|
if (formNotificationId) {
|
|
98
98
|
formNotificationId.style.display = 'block';
|
|
99
|
+
formNotificationId.classList.add('notification');
|
|
99
100
|
formNotificationId.classList.add(cssClass);
|
|
100
101
|
|
|
101
102
|
const errorElement = id('error');
|
|
@@ -103,7 +104,12 @@ const processFormDataAction = (cssClass, message, formNotificationId) => {
|
|
|
103
104
|
errorElement.scrollIntoView({ behavior: 'smooth' });
|
|
104
105
|
errorElement.style.color = 'white';
|
|
105
106
|
errorElement.innerHTML = message;
|
|
107
|
+
} else {
|
|
108
|
+
// Ensure we scroll to the notification if no specific error element exists
|
|
109
|
+
formNotificationId.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
106
110
|
}
|
|
111
|
+
formNotificationId.innerHTML = message;
|
|
112
|
+
|
|
107
113
|
const loader = id('setLoader');
|
|
108
114
|
if (loader) loader.classList.remove('loader');
|
|
109
115
|
} else {
|
|
@@ -161,11 +167,14 @@ export const getApiData = async (URL, token = null) => {
|
|
|
161
167
|
headers: {
|
|
162
168
|
"X-Requested-With": "XMLHttpRequest",
|
|
163
169
|
"Content-Type": "application/json",
|
|
164
|
-
Accept: "application/json"
|
|
165
|
-
...(token && { Authorization: `Bearer ${token}` }), // only add if token exists
|
|
170
|
+
Accept: "application/json"
|
|
166
171
|
},
|
|
167
172
|
};
|
|
168
173
|
|
|
174
|
+
if (token) {
|
|
175
|
+
config.headers['Authorization'] = `Bearer ${token}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
169
178
|
const response = await axios.get(URL, config);
|
|
170
179
|
return response.data;
|
|
171
180
|
} catch (error) {
|
|
@@ -202,11 +211,14 @@ export const getMultipleApiData = async (url1, url2, token = null) => {
|
|
|
202
211
|
headers: {
|
|
203
212
|
"X-Requested-With": "XMLHttpRequest",
|
|
204
213
|
"Content-Type": "application/json",
|
|
205
|
-
Accept: "application/json"
|
|
206
|
-
...(token && { Authorization: `Bearer ${token}` }), // add only if token exists
|
|
214
|
+
Accept: "application/json"
|
|
207
215
|
},
|
|
208
216
|
};
|
|
209
217
|
|
|
218
|
+
if (token) {
|
|
219
|
+
config.headers['Authorization'] = `Bearer ${token}`;
|
|
220
|
+
}
|
|
221
|
+
|
|
210
222
|
// run requests in parallel
|
|
211
223
|
const [res1, res2] = await axios.all([
|
|
212
224
|
axios.get(url1, config),
|
package/Loader.js
CHANGED
|
@@ -24,14 +24,17 @@ export const clearLoader = (elementId = 'setLoader', loaderClass = "loader") =>
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
loader.style.display = "none";
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
loaderClass.split(' ').forEach(cls => {
|
|
28
|
+
if (cls) loader.classList.remove(cls);
|
|
29
|
+
});
|
|
29
30
|
}
|
|
30
31
|
export const showLoader = (elementId ='setLoader', loaderClass = "loader") => {
|
|
31
32
|
const loader = id(elementId);
|
|
32
33
|
if (!loader) {
|
|
33
34
|
throw new Error(`Element not found`);
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
+
loaderClass.split(' ').forEach(cls => {
|
|
37
|
+
if (cls) loader.classList.add(cls);
|
|
38
|
+
});
|
|
36
39
|
loader.style.display = "block";
|
|
37
40
|
};
|
package/ShowResponse.js
CHANGED
|
@@ -22,16 +22,22 @@ export const showNotification = (
|
|
|
22
22
|
message,
|
|
23
23
|
timer = 5000,
|
|
24
24
|
) => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
const el = id(`${elementId}`);
|
|
26
|
+
if (!el) return;
|
|
27
|
+
|
|
28
|
+
el.style.display = 'block';
|
|
29
|
+
el.classList.add(addClass);
|
|
30
|
+
el.innerHTML = message;
|
|
31
|
+
|
|
32
|
+
const loaderEl = id('loader');
|
|
33
|
+
if (loaderEl) loaderEl.classList.remove('loader');
|
|
30
34
|
|
|
31
35
|
setTimeout(() => {
|
|
32
|
-
id(`${elementId}`)
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
const target = id(`${elementId}`);
|
|
37
|
+
if (!target) return;
|
|
38
|
+
target.style.backgroundColor = '';
|
|
39
|
+
target.style.color = '';
|
|
40
|
+
target.innerHTML = '';
|
|
35
41
|
}, timer);
|
|
36
42
|
};
|
|
37
43
|
|
|
@@ -39,10 +45,10 @@ export const showNotification = (
|
|
|
39
45
|
|
|
40
46
|
export const showResponse = (theId, message, status) => {
|
|
41
47
|
const responseEl = id(theId);
|
|
42
|
-
|
|
48
|
+
if (!responseEl) return;
|
|
43
49
|
|
|
50
|
+
const col = status ? 'green' : 'red';
|
|
44
51
|
responseEl.innerHTML = message;
|
|
45
|
-
responseEl.style.color = 'green';
|
|
46
52
|
responseEl.style.backgroundColor = col;
|
|
47
53
|
responseEl.style.color = 'white';
|
|
48
54
|
setTimeout(() => {
|
package/axiosWrapper.js
CHANGED
|
@@ -4,16 +4,17 @@ import axiosRetry from 'axios-retry';
|
|
|
4
4
|
|
|
5
5
|
axiosRetry(axios, { retries: 3 });
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// Ensure Axios always sends cookies (like the HttpOnly auth token) with every request
|
|
8
|
+
axios.defaults.withCredentials = true;
|
|
8
9
|
|
|
9
10
|
axios.interceptors.request.use(config => {
|
|
10
11
|
config.headers['X-Requested-With'] = 'XMLHttpRequest';
|
|
11
|
-
// config.headers['Accept'] = 'application/json';
|
|
12
12
|
|
|
13
|
+
const csrfToken = getCookieValue('XSRF-TOKEN');
|
|
13
14
|
if (csrfToken) {
|
|
14
15
|
config.headers['X-XSRF-TOKEN'] = csrfToken;
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
+
|
|
17
18
|
return config;
|
|
18
19
|
});
|
|
19
20
|
|
package/config/version.php
CHANGED
package/general.js
CHANGED
|
@@ -14,6 +14,8 @@ export const matchInput = (first, second, err) => {
|
|
|
14
14
|
firstInput = id(first);
|
|
15
15
|
secondInput = id(second);
|
|
16
16
|
|
|
17
|
+
if (!error || !firstInput || !secondInput) return;
|
|
18
|
+
|
|
17
19
|
secondInput.addEventListener('keyup', () => {
|
|
18
20
|
|
|
19
21
|
if (secondInput.value !== firstInput.value) {
|
|
@@ -61,10 +63,12 @@ export const parseErrorResponse = (error) => {
|
|
|
61
63
|
|
|
62
64
|
export const showFileName = (fileCss) => {
|
|
63
65
|
const fileInput = document.querySelector(`#${fileCss} input[type=file]`);
|
|
66
|
+
if (!fileInput) return;
|
|
67
|
+
|
|
64
68
|
fileInput.onchange = () => {
|
|
65
69
|
if (fileInput.files.length > 0) {
|
|
66
70
|
const fileName = document.querySelector(`#${fileCss} .file-name`);
|
|
67
|
-
fileName.textContent = fileInput.files[0].name;
|
|
71
|
+
if (fileName) fileName.textContent = fileInput.files[0].name;
|
|
68
72
|
}
|
|
69
73
|
};
|
|
70
74
|
}
|
package/index.js
CHANGED
package/package.json
CHANGED
package/release-history.md
CHANGED
|
@@ -101,3 +101,21 @@
|
|
|
101
101
|
|
|
102
102
|
- ๐ Bump version to v1.2.74 _(by modernman00)_
|
|
103
103
|
- ๐งน Auto-commit before release _(by modernman00)_
|
|
104
|
+
|
|
105
|
+
## v1.2.76 โ 2026-07-17 17:47
|
|
106
|
+
### Changelog for v1.2.76
|
|
107
|
+
|
|
108
|
+
- ๐ Bump version to v1.2.76 _(by modernman00)_
|
|
109
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
110
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
111
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
112
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
113
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
114
|
+
- refactor: improve robustness of UI utilities and add comprehensive unit test coverage _(by modernman00)_
|
|
115
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|
|
116
|
+
|
|
117
|
+
## v1.2.77 โ 2026-07-17 22:53
|
|
118
|
+
### Changelog for v1.2.77
|
|
119
|
+
|
|
120
|
+
- ๐ Bump version to v1.2.77 _(by modernman00)_
|
|
121
|
+
- ๐งน Auto-commit before release _(by modernman00)_
|