@modernman00/shared-js-lib 1.2.74 โ†’ 1.2.76

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 CHANGED
@@ -18,7 +18,8 @@ export function createCodeSubmitHandler({
18
18
  redirect,
19
19
  theme,
20
20
  lengthLimit: lengthLimitArray,
21
- optionalFields: [inputId],
21
+ optionalFields: [],
22
22
  recaptchaAction
23
23
  });
24
24
  }
25
+ // testing
package/AcctMgt/forgot.js CHANGED
@@ -18,7 +18,7 @@ export function forgotSubmitHandler({
18
18
  redirect,
19
19
  theme,
20
20
  lengthLimit: lengthLimitArray,
21
- optionalFields: [inputId],
21
+ optionalFields: [],
22
22
  recaptchaAction
23
23
  });
24
24
  }
package/AcctMgt/login.js CHANGED
@@ -42,7 +42,7 @@ export function createAdminLoginHandler({
42
42
  redirect,
43
43
  theme,
44
44
  lengthLimit: lengthLimitArray,
45
- optionalFields: [emailId, passwordId, ...optionalFields],
45
+ optionalFields: [...optionalFields],
46
46
  recaptchaAction
47
47
  });
48
48
  }
@@ -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
- alert('The form cannot be submitted. Please check the errors');
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
- const seconds = (Date.now() - new Date(entry.dataset.time).getTime()) / 1000;
49
- entry.textContent = getDuration(seconds);
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 { interval, epoch } = getDuration(timeAgoInSeconds);
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(date)
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
- id(uploadBtn).addEventListener('click', () => input.click());
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
- loader.classList.remove(loaderClass);
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
- loader.classList.add(loaderClass);
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
- // display the success information for 10sec
26
- id(`${elementId}`).style.display = 'block'; // unblock the notification
27
- id(`${elementId}`).classList.add(addClass); // add the success class
28
- id(`${elementId}`).innerHTML = message; // error element
29
- id('loader').classList.remove('loader'); // remove loader
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}`).style.backgroundColor = '';
33
- id(`${elementId}`).style.color = '';
34
- id(`${elementId}`).innerHTML = '';
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
- const col = status ? 'green' : 'red';
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
- const csrfToken = getCookieValue('XSRF-TOKEN');
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
 
@@ -1,3 +1,3 @@
1
1
  <?php
2
- define('APP_VERSION', 'v1.2.74');
2
+ define('APP_VERSION', 'v1.2.76');
3
3
 
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
@@ -4,7 +4,6 @@ export * from './Utility.js';
4
4
  export * from './Loader.js';
5
5
  export * from './Cookie.js';
6
6
  export * from './FormHelper.js'
7
- export * from './Loader.js';
8
7
  export * from './ShowResponse.js';
9
8
  export * from './DateTime.js';
10
9
  export * from './CountryCode.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modernman00/shared-js-lib",
3
- "version": "1.2.74",
3
+ "version": "1.2.76",
4
4
  "description": "Reusable JS utilities for numerous js problems",
5
5
  "homepage": "https://github.com/modernman00/shared-js-lib#readme",
6
6
  "keywords": [
@@ -101,3 +101,15 @@
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)_