// Declare jQuery variables declare var jQuery: any; declare var $: any; import './style.scss'; (function (w, d, $) { const { __, _x, sprintf } = w.wp.i18n; // DOM ready $(function () { const feedId = w.adtObj.feed_id; let feedData = w.adtObj.feed_data; let feedStatus = feedData?.status || ''; let heartbeatInterval: NodeJS.Timeout; // Function to get the current tab from URL or form const getCurrentTab = (): string => { const currentParams = new URLSearchParams(window.location.search); return currentParams.get('tab') || 'general'; }; // Function to show WordPress style admin notices const showAdminNotice = ( message: string, type: 'error' | 'info' | 'warning' | 'success', id: string = '', loading: boolean = false ): void => { // Remove any existing notice with our specific class $('.woosea-admin-notice').remove(); const loadingHtml = loading ? '' : ''; // Create a new notice with WordPress standard classes const noticeClass = `notice notice-${type} is-dismissible woosea-admin-notice`; const noticeHtml = `

${loadingHtml}${message}

`; // Insert the notice at the top of the form $('#adt-edit-feed .tab-content').prepend(noticeHtml); // Make the dismiss button work $('.woosea-admin-notice .notice-dismiss').on('click', function () { // @ts-ignore $(this) .closest('.woosea-admin-notice') .fadeOut(300, function () { // @ts-ignore $(this).remove(); }); }); }; // Function to check if we're on the create new feed page (no feed ID in URL) const isCreateNewFeedPage = (): boolean => { return !new URLSearchParams(window.location.search).has('id'); }; // Function to validate project name without showing notices const validateProjectName = (showNotices: boolean = false): boolean => { // Only validate if project name field exists (only on General tab) if (!$('#projectname').length) { return true; // Skip validation if field doesn't exist } const input = $('#projectname'); const inputValue = input.val(); // Handle potential undefined value const value = typeof inputValue === 'string' ? inputValue : ''; const re = /^[a-zA-Z0-9-_.àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇߨøÅ寿œ ]*$/; const minLength = 3; const maxLength = 30; const is_projectname = re.test(value); // Check for allowed characters if (!is_projectname) { if (showNotices) { showAdminNotice( __( 'Sorry, only letters, numbers, whitespaces, -, . and _ are allowed for the projectname', 'woo-product-feed-pro' ), 'error' ); } return false; } else { // Check for length of projectname if (value.length < minLength) { if (showNotices) { showAdminNotice( sprintf( /* translators: %d: minimum length of project name */ __('Sorry, your project name needs to be at least %d characters long.', 'woo-product-feed-pro'), minLength ), 'error' ); } return false; } else if (value.length > maxLength) { if (showNotices) { showAdminNotice( sprintf( /* translators: %d: maximum length of project name */ __('Sorry, your project name cannot be over %d characters long.', 'woo-product-feed-pro'), maxLength ), 'error' ); } return false; } else { // Valid project name return true; } } }; // Function to check if required fields are filled in for new feed creation const validateRequiredFields = (): boolean => { const currentTab = getCurrentTab(); // Only perform this validation on the final tab for new feeds if (isCreateNewFeedPage() && currentTab === 'conversion_analytics') { // Get temporary feed data via AJAX let allFieldsValid = true; let errorMessages: string[] = []; // Make an AJAX call to check other required fields that might be stored in the temp data $.ajax({ url: w.ajaxurl, type: 'POST', async: false, // Make this synchronous so we get the result immediately data: { action: 'check_temp_feed_required_fields', nonce: w.adtObj.adtNonce, }, success: function (response: any) { if (response.success === false) { allFieldsValid = false; // Add server-side validation errors to our list if (response.data && response.data.errors && Array.isArray(response.data.errors)) { response.data.errors.forEach((error: string) => { errorMessages.push(error); }); } } }, error: function () { allFieldsValid = false; errorMessages.push(__('Could not validate feed configuration. Please try again.', 'woo-product-feed-pro')); }, }); // Show error messages if validation failed if (!allFieldsValid) { showAdminNotice(errorMessages.join('
'), 'error'); } return allFieldsValid; } // For other tabs or existing feeds, return true return true; }; /** * Run heartbeat to check if the feed is processing. * @returns void */ const heartbeat = () => { if (!heartbeatInterval) { // Start heartbeat. heartbeatInterval = setInterval(() => { checkFeedStatus(feedId); }, 10000); } }; /** * Check the status of the feed. * @returns void */ const checkFeedStatus = (feedId: number) => { const feedIds = [feedId]; $.ajax({ url: w.ajaxurl, type: 'POST', data: { action: 'adt_get_feed_processing_status', nonce: w.adtObj.adtNonce, feed_ids: feedIds, }, }).done((response: any) => { if (response.success) { // Find the data array in the response that has the same feed ID. const feedDataObj = Array.isArray(response.data) ? response.data.find((data: any) => String(data.feed_id) === String(feedId)) : null; feedStatus = feedDataObj?.status || ''; updateFeedProcessingNotice(); updateContainerStatus(); updateFormSubmitButton(); } }); }; /** * Disable the form submit button. * @returns void */ const updateFormSubmitButton = (): void => { const status = feedStatus === 'processing' ? 'disabled' : 'enabled'; // Check if there is an input or button with type="submit". const $form = $('.adt-edit-feed-form'); $form.find('button[type="submit"]').prop('disabled', status === 'disabled'); }; /** * Update the container status. * @returns void */ const updateContainerStatus = (): void => { const $container = $('#adt-edit-feed'); $container.attr('data-status', feedStatus); }; /** * Show or hide a notice if the feed is processing. * @returns void */ const updateFeedProcessingNotice = (): void => { if (feedStatus === 'processing') { showAdminNotice( __( 'Feed is currently processing. Please wait for the feed generation to complete before making changes.', 'woo-product-feed-pro' ), 'error', 'feed-processing-notice', true ); } else { $('.woosea-admin-notice#feed-processing-notice').remove(); } }; /** * Check the status of the feed on load. * @returns void */ const checkFeedStatusOnLoad = () => { if (!isCreateNewFeedPage()) { heartbeat(); updateFeedProcessingNotice(); updateFormSubmitButton(); } }; // Check the status of the feed on load. checkFeedStatusOnLoad(); // Handle form submission // @ts-ignore $('.adt-edit-feed-form').on('submit', function (e) { const currentTab = getCurrentTab(); // Validate based on current tab if (currentTab === 'general') { // On General tab, validate project name const isValid = validateProjectName(true); if (!isValid) { e.preventDefault(); return false; } } else if (currentTab === 'conversion_analytics' && isCreateNewFeedPage()) { // On final tab for new feeds, validate all required fields if (!validateRequiredFields()) { e.preventDefault(); // Scroll to the top to show the error message window.scrollTo(0, 0); return false; } } // Check rule value if it exists (for filters & rules tab) if ($('#rulevalue').length) { const ruleValue = $('#rulevalue').val(); const value = typeof ruleValue === 'string' ? ruleValue : ''; const minLength = 1; const maxLength = 200; if (value.length < minLength) { showAdminNotice(__('Sorry, rule value minimum length is 1 character', 'woo-product-feed-pro'), 'error'); e.preventDefault(); return false; } else if (value.length > maxLength) { showAdminNotice( sprintf( /* translators: %d: maximum length of rule value */ __('Sorry, rule value cannot be over %d characters long.', 'woo-product-feed-pro'), maxLength ), 'error' ); e.preventDefault(); return false; } } // Let form submit normally if validation passes return true; }); // Function to check if the target URL is navigating to another tab within the same edit feed page const isNavigatingToSameEditPage = (targetUrl: string): boolean => { // If there's no URL, we can't determine, so return false if (!targetUrl) return false; // If it's not an HTTP URL (e.g., javascript:void(0)), it's not navigating away if (!targetUrl.startsWith('http')) return false; try { const currentUrl = new URL(window.location.href); const destination = new URL(targetUrl); // Check if we're staying on the edit feed page const currentPageParam = currentUrl.searchParams.get('page'); const destPageParam = destination.searchParams.get('page'); // If we're navigating to the same edit feed page, don't show warning return destPageParam === 'adt-edit-feed'; } catch (e) { // If parsing fails, assume we're navigating away return false; } }; // Variable to track if we're submitting the form let isSubmittingForm = false; // Only add the beforeunload handler if we're on the create new feed page if (isCreateNewFeedPage()) { // Track when the form is being submitted to avoid showing the warning $('.adt-edit-feed-form').on('submit', function () { isSubmittingForm = true; }); // Track clicks on tab navigation links to avoid showing warnings $('.woo-product-feed-pro-nav-tab-wrapper .nav-tab').on('click', function () { // @ts-ignore // The link is within the edit feed page tabs, so temporarily disable the warning const linkHref = $(this).attr('href'); if (linkHref && isNavigatingToSameEditPage(linkHref)) { // Create a flag to bypass the warning isSubmittingForm = true; // Reset the flag after the page load happens setTimeout(() => { isSubmittingForm = false; }, 100); } }); // Add the beforeunload event handler // @ts-ignore $(window).on('beforeunload', function (e) { // Don't show warning if the form is being submitted or navigating to same page if (!isSubmittingForm) { // The returned string is ignored in modern browsers // They show a standard message instead const confirmationMessage = __( 'Leaving this page will result in losing your feed configuration. Are you sure you want to leave?', 'woo-product-feed-pro' ); // For older browsers that support returnValue // Use type assertion to avoid TypeScript errors (e as any).returnValue = confirmationMessage; return confirmationMessage; } }); } }); })(window, document, jQuery);