/** * MyAi Main Application Logic * * Page-specific form handlers for contact and subscribe forms. * Uses shared utilities from: i18n.js, utils/form-helpers.js, utils/api.js, modules/cookie-consent.js * * Depends on: jQuery, i18n.js (translation dict), shared utility modules */ (function ($) { "use strict"; /* ============================================================ PAGE INITIALIZATION ============================================================ */ // Set footer year $('#year').text(new Date().getFullYear()); // Load and display app version $.getJSON('/api/health/version').done(function (data) { if (data && data.version) { $('#app-version').text('v' + data.version); } }); // Initialize language using shared utility window.MyAi.applyLanguage(window.MyAi.currentLang()); $('.lang-flag').on('click', function () { window.MyAi.applyLanguage($(this).data('lang')); }); /* ============================================================ HEADER / NAVIGATION ============================================================ */ /** * Toggle mobile hamburger menu. */ $('#menuToggle').on('click', function () { var $nav = $('#mainNav'); var open = !$nav.hasClass('is-open'); $nav.toggleClass('is-open', open); $(this).attr('aria-expanded', open ? 'true' : 'false'); }); /** * Close mobile menu when a nav link is clicked. */ $('.nav a').on('click', function () { $('#mainNav').removeClass('is-open'); $('#menuToggle').attr('aria-expanded', 'false'); }); /* ============================================================ FORM HELPERS (imported from utils/form-helpers.js) ============================================================ */ // showFieldError, clearFieldErrors, isValidEmail, extractApiError // are now loaded from utils/form-helpers.js and exposed on window.MyAi /* ============================================================ CONTACT FORM ============================================================ */ /* ============================================================ CONTACT FORM HANDLER ============================================================ */ /** * Display success message and reset form. */ function formSuccess() { $('#contactForm')[0].reset(); submitMSG(true, window.MyAi.t('form.thanks')); } /** * Play shake animation on form error. */ function formError() { $('#contactForm').removeClass().addClass('contact-form shake').one('animationend', function () { $(this).removeClass('shake'); }); } /** * Update form message element with success/error state and message. * @param {boolean} valid - True for success state, false for error * @param {string} msg - Message to display * @param {string} [severity] - CSS class suffix for color ('danger', 'warning', etc.) */ function submitMSG(valid, msg, severity) { var tone = valid ? 'text-success' : ('text-' + (severity || 'danger')); $('#msgSubmit').removeClass().addClass('form-message ' + tone).text(msg); } /** * Handle contact form submission. */ $('#contactForm').on('submit', function (event) { event.preventDefault(); var loader = $('#contactLoader'), button = $('#submit'); var name = $('#name').val().trim(); var email = $('#email').val().trim(); var message = $('#message').val().trim(); window.MyAi.clearFieldErrors(['nameError', 'emailError', 'messageError']); $('#msgSubmit').removeClass().addClass('form-message').text(''); // Validate inputs var hasError = false; if (!name) { window.MyAi.showFieldError('nameError', window.MyAi.t('form.required.name')); hasError = true; } if (!email) { window.MyAi.showFieldError('emailError', window.MyAi.t('form.required.email')); hasError = true; } else if (!window.MyAi.isValidEmail(email)) { window.MyAi.showFieldError('emailError', window.MyAi.t('form.required.emailInvalid')); hasError = true; } if (!message) { window.MyAi.showFieldError('messageError', window.MyAi.t('form.required.message')); hasError = true; } if (hasError) return; loader.addClass('loader-visible'); button.prop('disabled', true); /** * Post contact form with captcha token. */ function postContact(token) { var payload = { Name: name, Email: email, Subject: '[MyAi.ro contact request]', Message: message, CaptchaToken: token || '' }; $.ajax({ type: 'POST', url: '/api/contact', data: JSON.stringify(payload), contentType: 'application/json; charset=utf-8', dataType: 'json' }).done(function (resp) { if (resp && resp.ok === true) formSuccess(); else submitMSG(false, window.MyAi.t('form.captchaFailed')); }).fail(function (jqXHR) { submitMSG(false, window.MyAi.extractApiError(jqXHR.responseJSON, jqXHR.status, 'form.failed'), jqXHR.status === 429 ? 'warning' : 'danger'); formError(); }).always(function () { loader.removeClass('loader-visible'); button.prop('disabled', false); }); } if (window.grecaptcha && window.MyAi.getReCaptchaSiteKey()) { grecaptcha.ready(function () { grecaptcha.execute(window.MyAi.getReCaptchaSiteKey(), { action: 'contact' }).then(postContact); }); } else { submitMSG(false, window.MyAi.t('form.captchaFailed')); formError(); loader.removeClass('loader-visible'); button.prop('disabled', false); } }); /* ============================================================ SUBSCRIBE FORM HANDLER ============================================================ */ /** * Handle subscribe form submission. */ $('#subscribeForm').on('submit', function (event) { event.preventDefault(); var $msg = $('#subscribeMsg'); var $button = $('#subscribeSubmit'); var $loader = $('#contactLoader'); var email = $('#subscribeEmail').val().trim(); var consent = $('#subscribeConsent').is(':checked'); /** * Update subscribe form message. */ function setMsg(severity, key) { $msg.removeClass().addClass('form-message text-' + severity).text(window.MyAi.t(key)); } window.MyAi.clearFieldErrors(['subscribeEmailError', 'subscribeConsentError']); $msg.removeClass().addClass('form-message').text(''); // Validate inputs var hasError = false; if (!email) { window.MyAi.showFieldError('subscribeEmailError', window.MyAi.t('form.required.email')); hasError = true; } else if (!window.MyAi.isValidEmail(email)) { window.MyAi.showFieldError('subscribeEmailError', window.MyAi.t('form.required.emailInvalid')); hasError = true; } if (!consent) { window.MyAi.showFieldError('subscribeConsentError', window.MyAi.t('subscribe.noConsent')); hasError = true; } if (hasError) return; $loader.addClass('loader-visible'); $button.prop('disabled', true); /** * Post subscribe with captcha token. */ function postSubscribe(token) { $.ajax({ type: 'POST', url: '/api/contact/subscribe', data: JSON.stringify({ Email: email, CaptchaToken: token || '' }), contentType: 'application/json; charset=utf-8', dataType: 'json' }).done(function (resp) { if (resp && resp.ok === true) { $('#subscribeForm')[0].reset(); setMsg('success', 'subscribe.thanks'); } else { setMsg('danger', 'form.captchaFailed'); } }).fail(function (jqXHR) { $msg.removeClass().addClass('form-message text-' + (jqXHR.status === 429 ? 'warning' : 'danger')) .text(window.MyAi.extractApiError(jqXHR.responseJSON, jqXHR.status, 'subscribe.failed')); }).always(function () { $loader.removeClass('loader-visible'); $button.prop('disabled', false); }); } if (window.grecaptcha && window.MyAi.getReCaptchaSiteKey()) { grecaptcha.ready(function () { grecaptcha.execute(window.MyAi.getReCaptchaSiteKey(), { action: 'subscribe' }).then(postSubscribe); }); } else { $loader.removeClass('loader-visible'); $button.prop('disabled', false); setMsg('danger', 'form.captchaFailed'); } }); /* ============================================================ COOKIE CONSENT (handled by modules/cookie-consent.js) ============================================================ */ // Cookie banner, consent handlers, and storage are managed by // modules/cookie-consent.js and exposed via window.MyAi /* ============================================================ API & CONFIGURATION LOADING (from utils/api.js) ============================================================ */ // API utilities (checkApiLive, getRecaptchaWebKey, getGoogleTagManagerId, loadGoogleTagManager) // are loaded from utils/api.js and exposed via window.MyAi /* ============================================================ STARTUP ============================================================ */ $(window).on('load', function () { $.when( window.MyAi.checkApiLive(), window.MyAi.getRecaptchaWebKey(), window.MyAi.getGoogleTagManagerId() ).always(window.MyAi.initConsent); }); })(jQuery);