declare var jQuery: any; declare var toastr: any; import './style.scss'; interface ReviewAttribute { attribute: string; value: string; is_static: boolean; is_empty: boolean; } interface ReviewFeed { id: string; title: string; source_channel: string; channel: string; file_format: string; refresh_interval: string; attributes: ReviewAttribute[]; filters: number; warnings: string[]; } interface ImportedFeed { title: string; edit_url: string; } (function (w, $) { const { __, _n, sprintf } = w.wp.i18n; $(function () { let isProcessing = false; /** * Send a migration request for one source. */ function request( row: any, step: 'review' | 'import', feedIds: string[], button: any, onSuccess: (data: any) => void, onError?: () => void ) { if (isProcessing) { return; } isProcessing = true; let result: any = null; const loader = button.next('.adt-loader'); button.prop('disabled', true); row.attr('aria-busy', 'true'); loader.show(); $.ajax({ url: w.ajaxurl, type: 'POST', data: { action: 'adt_feed_migration', nonce: w.adtFeedMigration.nonce, step: step, source: row.data('source'), feed_ids: feedIds, }, success: function (response: any) { if (response.success) { result = response.data; } else { toastr.error(response.data?.message || __('Operation failed.', 'woo-product-feed-pro')); } }, error: function () { toastr.error(__('Operation failed.', 'woo-product-feed-pro')); }, complete: function () { isProcessing = false; button.prop('disabled', false); row.removeAttr('aria-busy'); loader.hide(); // Called after the reset above, so that the callback can send the next request. if (null !== result) { onSuccess(result); } else if (onError) { onError(); } }, }); } /** * Build the review panel for one feed. */ function renderFeedReview(feed: ReviewFeed) { const box = $('
'); box.append($('

').text(feed.title)); const summary = $(''); const items: [string, string][] = [ [__('Channel', 'woo-product-feed-pro'), sprintf('%s → %s', feed.source_channel, feed.channel)], [__('File format', 'woo-product-feed-pro'), feed.file_format], [__('Refresh interval', 'woo-product-feed-pro'), feed.refresh_interval], [__('Filters', 'woo-product-feed-pro'), String(feed.filters)], ]; items.forEach(([label, value]) => { summary.append( $('
  • ') /* translators: %s: label of a feed setting. */ .append($('').text(sprintf(__('%s:', 'woo-product-feed-pro'), label))) .append(document.createTextNode(' ' + value)) ); }); box.append(summary); if (feed.warnings.length) { const warnings = $(''); feed.warnings.forEach((warning) => warnings.append($('
  • ').text(warning))); box.append(warnings); } const table = $( '
    ' ); table.find('th').eq(0).text(__('Feed attribute', 'woo-product-feed-pro')); table.find('th').eq(1).text(__('Value', 'woo-product-feed-pro')); feed.attributes.forEach((attribute) => { const value = attribute.is_empty ? __('No equivalent field. Select one after the import.', 'woo-product-feed-pro') : attribute.is_static ? /* translators: %s: static attribute value. */ sprintf(__('Static value: %s', 'woo-product-feed-pro'), attribute.value) : attribute.value; const tr = $('').toggleClass('adt-feed-migration-unmapped', attribute.is_empty); tr.append($('').text(attribute.attribute)); tr.append($('').text(value)); table.find('tbody').append(tr); }); box.append(table); return box; } // Select all. $('.adt-feed-migration-select-all').on('change', function (this: HTMLInputElement) { $(this).closest('.adt-feed-migration-source').find('.adt-feed-migration-feed').prop('checked', this.checked); }); // Step 1: review the selected feeds. $('.adt-feed-migration-review-button').on('click', function (this: HTMLElement) { const button = $(this); const row = button.closest('.adt-feed-migration-source'); const feedIds = row .find('.adt-feed-migration-feed:checked') .map(function (this: HTMLInputElement) { return this.value; }) .get(); if (!feedIds.length) { toastr.error(__('Select at least one feed.', 'woo-product-feed-pro')); return; } request(row, 'review', feedIds, button, function (data: { feeds: ReviewFeed[] }) { const review = row.find('.adt-feed-migration-review').empty(); review.append( $('

    ').text( __( 'Check how each feed maps before you import it. Items in the warning lists are not imported as they are.', 'woo-product-feed-pro' ) ) ); data.feeds.forEach((feed) => review.append(renderFeedReview(feed))); const actions = $('
    '); const importButton = $( '' ).text( sprintf( /* translators: %d: number of feeds. */ _n('Import %d feed', 'Import %d feeds', data.feeds.length, 'woo-product-feed-pro'), data.feeds.length ) ); const backButton = $('').text( __('Back', 'woo-product-feed-pro') ); actions.append(importButton, backButton, $('')); review.append(actions); row.find('.adt-feed-migration-select').prop('hidden', true); review.prop('hidden', false).attr('tabindex', '-1').trigger('focus'); backButton.on('click', function () { review.prop('hidden', true).empty(); row.find('.adt-feed-migration-select').prop('hidden', false); row.find('.adt-feed-migration-review-button').trigger('focus'); }); /** * Show the import result in place of the review. */ function showResult(result: { feeds: ImportedFeed[]; failed: string[] }) { const box = row.find('.adt-feed-migration-result').empty(); if (result.failed.length) { const failed = $(''); result.failed.forEach((title) => failed.append($('
  • ').text(title))); box.append( $('

    ').text( __('These feeds could not be imported. Try again.', 'woo-product-feed-pro') ), failed ); } box.append( $('

    ').text( sprintf( /* translators: %d: number of imported feeds. */ _n( '%d feed was imported as a draft. Open it, check the settings, and activate it.', '%d feeds were imported as drafts. Open each one, check the settings, and activate it.', result.feeds.length, 'woo-product-feed-pro' ), result.feeds.length ) ) ); const list = $(''); result.feeds.forEach((feed) => { list.append($('
  • ').append($('').attr('href', feed.edit_url).text(feed.title))); }); box.append(list); review.prop('hidden', true).empty(); box.prop('hidden', false).attr('tabindex', '-1').trigger('focus'); if (result.feeds.length) { toastr.success(__('Feeds imported.', 'woo-product-feed-pro')); } } // Step 2: import. One request per feed, so that a server timeout stops at most one feed. importButton.on('click', function () { const result = { feeds: [] as ImportedFeed[], failed: [] as string[] }; const importNext = (index: number) => { if (index >= data.feeds.length) { showResult(result); return; } request( row, 'import', [data.feeds[index].id], importButton, function (feedResult: { feeds: ImportedFeed[]; failed: string[] }) { result.feeds.push(...feedResult.feeds); result.failed.push(...feedResult.failed); importNext(index + 1); }, function () { // The request failed, so this feed and the ones after it were not imported. data.feeds.slice(index).forEach((feed) => result.failed.push(feed.title)); showResult(result); } ); }; importNext(0); }); }); }); }); })(window as any, jQuery);