refactor: Extract shared JavaScript utilities (Step 1 of 6)
Create reusable utility modules to eliminate duplication across main.js, cv-matcher.js, and legal.js: - js/utils/form-helpers.js: showFieldError, clearFieldErrors, isValidEmail, extractApiError — shared form validation and error handling - js/utils/i18n.js: currentLang, t, applyLanguage, updateLegalLinks, browserLang — shared translation and language switching - js/utils/api.js: checkApiLive, getRecaptchaWebKey, getGoogleTagManagerId, loadGoogleTagManager — shared API configuration loading - js/modules/cookie-consent.js: getConsent, setConsent, initConsent, setupConsentHandlers — cookie banner and consent management All utilities exposed on window.MyAi namespace for use by existing pages. Full JSDoc headers and inline comments for maintainability. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* API & Configuration Loading Utilities
|
||||
*
|
||||
* Shared helpers for API health checks and configuration retrieval.
|
||||
* Handles reCaptcha key loading, Google Tag Manager setup, and API health checks.
|
||||
*/
|
||||
|
||||
var reCaptchaSiteKey = null;
|
||||
var gTagManagerId = null;
|
||||
|
||||
/**
|
||||
* Check API /health/live endpoint and update status indicator if present.
|
||||
* Updates #api-status element with "API: {status}" (green if OK, red if down).
|
||||
* Logs result to console if element not found.
|
||||
*
|
||||
* @returns {object} - jQuery AJAX promise
|
||||
*/
|
||||
function checkApiLive() {
|
||||
var $statusEl = $('#api-status');
|
||||
return $.ajax({
|
||||
url: '/api/health/live',
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
timeout: 5000
|
||||
}).done(function (data) {
|
||||
var status = (data && data.status) ? data.status : 'unknown';
|
||||
if ($statusEl.length) {
|
||||
$statusEl.text('API: ' + status).removeClass('text-danger').addClass('text-success');
|
||||
} else {
|
||||
console.log('API live status:', status);
|
||||
}
|
||||
}).fail(function (jqXHR, textStatus, errorThrown) {
|
||||
if ($statusEl.length) {
|
||||
$statusEl.text('API: offline').removeClass('text-success').addClass('text-danger');
|
||||
}
|
||||
console.error('API health check failed:', textStatus, errorThrown);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load reCaptcha public site key from /api/captcha endpoint.
|
||||
* Dynamically injects reCaptcha script if not already loaded.
|
||||
* Stores key on window for use by grecaptcha.execute().
|
||||
*
|
||||
* @returns {object} - jQuery AJAX promise
|
||||
*/
|
||||
function getRecaptchaWebKey() {
|
||||
return $.get('/api/captcha').done(function (res) {
|
||||
reCaptchaSiteKey = res;
|
||||
if (reCaptchaSiteKey && !window.__recaptcha_loaded) {
|
||||
window.__recaptcha_loaded = true;
|
||||
var script = document.createElement('script');
|
||||
script.setAttribute('src', 'https://www.google.com/recaptcha/api.js?render=' + reCaptchaSiteKey);
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
}).fail(function () {
|
||||
console.warn('Could not load reCaptcha site key from /api/captcha');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Google Tag Manager ID from /api/google/tagmanager endpoint.
|
||||
* Does not load GTM script—that happens in loadGoogleTagManager() once consent is given.
|
||||
*
|
||||
* @returns {object} - jQuery AJAX promise
|
||||
*/
|
||||
function getGoogleTagManagerId() {
|
||||
return $.get('/api/google/tagmanager').done(function (res) {
|
||||
gTagManagerId = res;
|
||||
}).fail(function () {
|
||||
console.warn('Could not load Google Tag Manager id from /api/google/tagmanager');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and inject Google Tag Manager script if consent is given and not already loaded.
|
||||
* Sets up dataLayer and injects GTM script from googleapis.com.
|
||||
* Called only after user accepts analytics consent.
|
||||
*
|
||||
* Requires gTagManagerId to be loaded first via getGoogleTagManagerId().
|
||||
*/
|
||||
function loadGoogleTagManager() {
|
||||
if (window.__gtm_loaded || !gTagManagerId) return;
|
||||
window.__gtm_loaded = true;
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
window.dataLayer.push({
|
||||
'gtm.start': new Date().getTime(),
|
||||
event: 'gtm.js'
|
||||
});
|
||||
var script = document.createElement('script');
|
||||
script.async = true;
|
||||
script.src = 'https://www.googletagmanager.com/gtm/js?id=' + gTagManagerId;
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently loaded reCaptcha site key.
|
||||
* @returns {string|null} - reCaptcha site key or null if not yet loaded
|
||||
*/
|
||||
function getReCaptchaSiteKey() {
|
||||
return reCaptchaSiteKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently loaded Google Tag Manager ID.
|
||||
* @returns {string|null} - GTM ID or null if not yet loaded
|
||||
*/
|
||||
function getGoogleTagManagerId() {
|
||||
return gTagManagerId;
|
||||
}
|
||||
|
||||
// Expose API utilities on window.MyAi for use by other scripts
|
||||
window.MyAi = window.MyAi || {};
|
||||
window.MyAi.checkApiLive = checkApiLive;
|
||||
window.MyAi.getRecaptchaWebKey = getRecaptchaWebKey;
|
||||
window.MyAi.getGoogleTagManagerId = getGoogleTagManagerId;
|
||||
window.MyAi.loadGoogleTagManager = loadGoogleTagManager;
|
||||
window.MyAi.getReCaptchaSiteKey = getReCaptchaSiteKey;
|
||||
window.MyAi.applyConsent = function(consent) {
|
||||
if (consent && consent.analytics === true) loadGoogleTagManager();
|
||||
};
|
||||
Reference in New Issue
Block a user