| @@ -256,8 +256,20 @@ | ||
| 256 | 256 | $(document.body).on('click', '.wpfnl-bulk-trash', this.bulkTrashFunnel) |
| 257 | 257 | $(document.body).on('click', '.wpfnl-bulk-delete', this.bulkDeleteFunnel) |
| 258 | 258 | $(document.body).on('click', '.wpfnl-bulk-restore', this.bulkRestoreFunnel) |
| 259 | 259 | $(document.body).on('click', '.wpfnl-bulk-export', this.bulkExportFunnel) |
| 260 | + // Bound directly, NOT delegated through document.body: the modal wrapper has its | |
| 261 | + // own click handler calling stopPropagation() to keep backdrop clicks from closing | |
| 262 | + // the modal, so events from inside it never reach body. | |
| 263 | + $('.wpfnl-import-cancel').on('click', this.CancelImport) | |
| 264 | + $('.wpfnl-import-view-funnels').on('click', function (e) { | |
| 265 | + e.preventDefault() | |
| 266 | + window.location.reload() | |
| 267 | + }) | |
| 268 | + | |
| 269 | + // An import started in an earlier page view keeps running in the background, so | |
| 270 | + // pick it back up rather than leaving the user with no sign of it. | |
| 271 | + resumeImportIfRunning() | |
| 260 | 272 | } |
| 261 | 273 | |
| 262 | 274 | /** |
| 263 | 275 | * Select quantity |
| @@ -476,8 +488,9 @@ | ||
| 476 | 488 | status_updated: 'Funnel status has been updated successfully.', |
| 477 | 489 | bulk_trash_success: 'The selected funnels have been moved to Trash.', |
| 478 | 490 | bulk_delete_success: 'The selected funnels have been deleted permanently.', |
| 479 | 491 | bulk_restore_success: 'The selected funnels have been restored successfully.', |
| 492 | + import_success: 'The funnels have been imported successfully.', | |
| 480 | 493 | } |
| 481 | 494 | |
| 482 | 495 | const toastKey = localStorage.getItem('wpfnl_show_toast') |
| 483 | 496 | if (!toastKey || !toastMap[toastKey]) return |
| @@ -822,8 +835,14 @@ | ||
| 822 | 835 | */ |
| 823 | 836 | FunnelHandler.prototype.ShowImportFunnelModal = function (e) { |
| 824 | 837 | e.stopPropagation() |
| 825 | 838 | e.preventDefault() |
| 839 | + // Reset back to the upload form: a previous run may have left the modal showing | |
| 840 | + // progress or a result card. | |
| 841 | + $('.wpfnl-import-progress').hide().removeClass('is-stalled') | |
| 842 | + $('.wpfnl-import-result').hide() | |
| 843 | + $('#wpfnl-import-funnels').show() | |
| 844 | + $('#wpfnl-export-import-warning').css('display', 'none') | |
| 826 | 845 | $('.import-funnel-modal').addClass('show-modal') |
| 827 | 846 | } |
| 828 | 847 | |
| 829 | 848 | /** |
| @@ -854,8 +873,14 @@ | ||
| 854 | 873 | */ |
| 855 | 874 | FunnelHandler.prototype.ShowImportFunnelModal = function (e) { |
| 856 | 875 | e.stopPropagation() |
| 857 | 876 | e.preventDefault() |
| 877 | + // Reset back to the upload form: a previous run may have left the modal showing | |
| 878 | + // progress or a result card. | |
| 879 | + $('.wpfnl-import-progress').hide().removeClass('is-stalled') | |
| 880 | + $('.wpfnl-import-result').hide() | |
| 881 | + $('#wpfnl-import-funnels').show() | |
| 882 | + $('#wpfnl-export-import-warning').css('display', 'none') | |
| 858 | 883 | $('.import-funnel-modal').addClass('show-modal') |
| 859 | 884 | } |
| 860 | 885 | |
| 861 | 886 | /** |
| @@ -878,9 +903,48 @@ | ||
| 878 | 903 | $('#wpfnl-export-import-warning').css('display', 'none') |
| 879 | 904 | } |
| 880 | 905 | |
| 881 | 906 | /** |
| 907 | + * Show a validation/error message inside the import funnel modal. | |
| 882 | 908 | * |
| 909 | + * The message element carries a default text, so it is always set | |
| 910 | + * explicitly here to avoid leaking a previous message. | |
| 911 | + * | |
| 912 | + * @param message | |
| 913 | + * @since 2.6.3 | |
| 914 | + */ | |
| 915 | + FunnelHandler.prototype.ShowImportWarning = function (message) { | |
| 916 | + $('#wpfnl-export-import-warning') | |
| 917 | + .text(message || 'Please select a valid file.') | |
| 918 | + .css('display', 'inline-block') | |
| 919 | + $('#wpfnl-import-funnel .wpfnl-loader').css('display', 'none') | |
| 920 | + } | |
| 921 | + | |
| 922 | + /** | |
| 923 | + * Pull the error message out of an ajax response. | |
| 924 | + * | |
| 925 | + * wp_send_json_error() responses arrive either as a parsed body | |
| 926 | + * (HTTP 200) or on the jqXHR when a status code was passed. | |
| 927 | + * | |
| 928 | + * @param response | |
| 929 | + * @since 2.6.3 | |
| 930 | + */ | |
| 931 | + FunnelHandler.prototype.GetImportErrorMessage = function (response) { | |
| 932 | + const body = response && response.responseJSON ? response.responseJSON : response | |
| 933 | + | |
| 934 | + if (body && body.data && body.data.message) { | |
| 935 | + return body.data.message | |
| 936 | + } | |
| 937 | + | |
| 938 | + if (body && typeof body.data === 'string') { | |
| 939 | + return body.data | |
| 940 | + } | |
| 941 | + | |
| 942 | + return '' | |
| 943 | + } | |
| 944 | + | |
| 945 | + /** | |
| 946 | + * | |
| 883 | 947 | * Ajax handler for import funnels |
| 884 | 948 | * funnel action |
| 885 | 949 | * |
| 886 | 950 | * @param event |
| @@ -887,8 +951,10 @@ | ||
| 887 | 951 | * @since 2.6.3 |
| 888 | 952 | */ |
| 889 | 953 | FunnelHandler.prototype.ImportFunnels = function (event) { |
| 890 | 954 | event.preventDefault() |
| 955 | + // `this` is the submitted form here, so reach the helpers via the prototype. | |
| 956 | + const handler = FunnelHandler.prototype | |
| 891 | 957 | $('#wpfnl-import-funnel .wpfnl-loader').css('display', 'inline-block') |
| 892 | 958 | $('#wpfnl-export-import-warning').css('display', 'none') |
| 893 | 959 | let formData = new FormData(this) |
| 894 | 960 | let importFile = $('#wpfnl-file-import')[0].files[0] |
| @@ -908,28 +974,40 @@ | ||
| 908 | 974 | dataType: 'json', |
| 909 | 975 | processData: false, |
| 910 | 976 | data: formData, |
| 911 | 977 | success: function (response) { |
| 912 | - window.location.reload(); | |
| 978 | + // wp_send_json_error() without a status code still returns HTTP 200. | |
| 979 | + if (response && false === response.success) { | |
| 980 | + handler.ShowImportWarning(handler.GetImportErrorMessage(response)) | |
| 981 | + return | |
| 982 | + } | |
| 913 | 983 | $('#wpfnl-import-funnel .wpfnl-loader').css('display', 'none') |
| 914 | - $('.import-funnel-modal').removeClass('show-modal') | |
| 984 | + // Deliberately no page reload here. The job has only been *queued* | |
| 985 | + // at this point, so reloading used to land the user on an unchanged | |
| 986 | + // funnel list and force them to keep refreshing to find out what | |
| 987 | + // happened. Stay open and follow the job instead. | |
| 988 | + showImportProgress() | |
| 989 | + pollImportStatus(response?.data?.run_id || '') | |
| 915 | 990 | }, |
| 916 | - }).done(function (request, status, XHR) { | |
| 917 | - if (status.success) { | |
| 918 | - // window.location.reload(); | |
| 919 | - } | |
| 920 | - $('#wpfnl-import-funnel .wpfnl-loader').css('display', 'none') | |
| 921 | - $('.import-funnel-modal').removeClass('show-modal') | |
| 991 | + error: function (jqXHR) { | |
| 992 | + // Validation failures are sent with a 4xx status code. | |
| 993 | + const body = | |
| 994 | + jqXHR && jqXHR.responseJSON ? jqXHR.responseJSON : null | |
| 995 | + // 409: a run really is still going. Offer to clear it rather than | |
| 996 | + // leaving the user stuck behind "Import already scheduled". | |
| 997 | + if (body && body.data && body.data.can_reset) { | |
| 998 | + showImportProgress() | |
| 999 | + pollImportStatus(body.data.run_id || '') | |
| 1000 | + return | |
| 1001 | + } | |
| 1002 | + handler.ShowImportWarning(handler.GetImportErrorMessage(jqXHR)) | |
| 1003 | + }, | |
| 922 | 1004 | }) |
| 923 | 1005 | } else { |
| 924 | - $('#wpfnl-export-import-warning').css('display', 'inline-block') | |
| 925 | - $('#wpfnl-export-import-warning').css('color', '#d63638 !important') | |
| 926 | - $('#wpfnl-import-funnel .wpfnl-loader').css('display', 'none') | |
| 1006 | + handler.ShowImportWarning('Please select a valid file.') | |
| 927 | 1007 | } |
| 928 | 1008 | } else { |
| 929 | - $('#wpfnl-export-import-warning').css('display', 'inline-block') | |
| 930 | - $('#wpfnl-export-import-warning').css('color', '#d63638 !important') | |
| 931 | - $('#wpfnl-import-funnel .wpfnl-loader').css('display', 'none') | |
| 1009 | + handler.ShowImportWarning('Please select a valid file.') | |
| 932 | 1010 | } |
| 933 | 1011 | } |
| 934 | 1012 | |
| 935 | 1013 | /** |
| @@ -955,10 +1033,9 @@ | ||
| 955 | 1033 | $('.import-funnel-modal .import-label > h4').text('Json file ready for import.') |
| 956 | 1034 | $('.import-funnel-modal .import-label > p').text(fileName) |
| 957 | 1035 | } else { |
| 958 | 1036 | $('.import-funnel-modal .import-label').removeClass('file-added') |
| 959 | - $('#wpfnl-export-import-warning').css('display', 'inline-block') | |
| 960 | - $('#wpfnl-export-import-warning').css('color', '#d63638 !important') | |
| 1037 | + FunnelHandler.prototype.ShowImportWarning('Please select a valid file.') | |
| 961 | 1038 | |
| 962 | 1039 | $('.import-funnel-modal .import-label > h4').html( |
| 963 | 1040 | 'Drag & Drop or <span class="primary-color">Choose file</span> to upload.', |
| 964 | 1041 | ) |
| @@ -967,10 +1044,9 @@ | ||
| 967 | 1044 | ) |
| 968 | 1045 | } |
| 969 | 1046 | } else { |
| 970 | 1047 | $('.import-funnel-modal .import-label').removeClass('file-added') |
| 971 | - $('#wpfnl-export-import-warning').css('display', 'inline-block') | |
| 972 | - $('#wpfnl-export-import-warning').css('color', '#d63638 !important') | |
| 1048 | + FunnelHandler.prototype.ShowImportWarning('Please select a valid file.') | |
| 973 | 1049 | |
| 974 | 1050 | $('.import-funnel-modal .import-label > h4').html( |
| 975 | 1051 | 'Drag & Drop or <span class="primary-color">Choose file</span> to upload.', |
| 976 | 1052 | ) |
| @@ -978,8 +1054,322 @@ | ||
| 978 | 1054 | } |
| 979 | 1055 | } |
| 980 | 1056 | |
| 981 | 1057 | /** |
| 1058 | + * Translated string with an English fallback. | |
| 1059 | + * | |
| 1060 | + * Strings in this file are historically hardcoded; new ones go through | |
| 1061 | + * WPFunnelVars.getText so they are actually translatable. | |
| 1062 | + * | |
| 1063 | + * @param key | |
| 1064 | + * @param fallback | |
| 1065 | + * | |
| 1066 | + * @since 2.5.16 | |
| 1067 | + */ | |
| 1068 | + function importText(key, fallback) { | |
| 1069 | + const bag = window.WPFunnelVars && WPFunnelVars.getText ? WPFunnelVars.getText : {} | |
| 1070 | + return bag[key] || fallback | |
| 1071 | + } | |
| 1072 | + | |
| 1073 | + /** | |
| 1074 | + * Swap the import modal into its progress state. | |
| 1075 | + * | |
| 1076 | + * @since 2.5.16 | |
| 1077 | + */ | |
| 1078 | + function showImportProgress() { | |
| 1079 | + $('.import-funnel-modal').addClass('show-modal') | |
| 1080 | + $('#wpfnl-import-funnels').hide() | |
| 1081 | + $('.wpfnl-import-result').hide() | |
| 1082 | + $('.wpfnl-import-progress').show() | |
| 1083 | + $('.wpfnl-import-progress').removeClass('is-stalled') | |
| 1084 | + $('.wpfnl-import-progress__label').text( | |
| 1085 | + importText('import_preparing', 'Preparing import…'), | |
| 1086 | + ) | |
| 1087 | + $('.wpfnl-import-progress__bar').css('width', '0%') | |
| 1088 | + $('.wpfnl-import-progress__meta').text('') | |
| 1089 | + } | |
| 1090 | + | |
| 1091 | + /** | |
| 1092 | + * Read the current import status. | |
| 1093 | + * | |
| 1094 | + * @param runId | |
| 1095 | + * | |
| 1096 | + * @since 2.5.16 | |
| 1097 | + */ | |
| 1098 | + function fetchImportStatus(runId) { | |
| 1099 | + const base = window.WPFunnelVars ? WPFunnelVars.rest_api_url : '' | |
| 1100 | + const url = | |
| 1101 | + base + | |
| 1102 | + 'wpfunnels/v1/import-export/import-status' + | |
| 1103 | + (runId ? '?run_id=' + encodeURIComponent(runId) : '') | |
| 1104 | + | |
| 1105 | + return fetch(url, { | |
| 1106 | + method: 'GET', | |
| 1107 | + headers: { 'X-WP-Nonce': WPFunnelVars.nonce }, | |
| 1108 | + }).then(function (response) { | |
| 1109 | + if (!response.ok) { | |
| 1110 | + throw new Error('status_request_failed') | |
| 1111 | + } | |
| 1112 | + return response.json() | |
| 1113 | + }) | |
| 1114 | + } | |
| 1115 | + | |
| 1116 | + /** | |
| 1117 | + * Paint one status tick into the progress UI. | |
| 1118 | + * | |
| 1119 | + * @param status | |
| 1120 | + * | |
| 1121 | + * @since 2.5.16 | |
| 1122 | + */ | |
| 1123 | + function renderImportProgress(status) { | |
| 1124 | + const pct = Math.max(0, Math.min(100, parseInt(status.pct, 10) || 0)) | |
| 1125 | + | |
| 1126 | + $('.wpfnl-import-progress__label').text( | |
| 1127 | + status.label || importText('import_in_progress', 'Importing funnels…'), | |
| 1128 | + ) | |
| 1129 | + $('.wpfnl-import-progress__bar').css('width', pct + '%') | |
| 1130 | + | |
| 1131 | + let meta = '' | |
| 1132 | + if (status.total_funnels) { | |
| 1133 | + meta = | |
| 1134 | + status.done_funnels + | |
| 1135 | + ' / ' + | |
| 1136 | + status.total_funnels + | |
| 1137 | + ' ' + | |
| 1138 | + importText('import_funnels_word', 'funnels') | |
| 1139 | + if (status.total_steps) { | |
| 1140 | + meta += | |
| 1141 | + ' · ' + | |
| 1142 | + status.done_steps + | |
| 1143 | + ' / ' + | |
| 1144 | + status.total_steps + | |
| 1145 | + ' ' + | |
| 1146 | + importText('import_steps_word', 'steps') | |
| 1147 | + } | |
| 1148 | + } | |
| 1149 | + $('.wpfnl-import-progress__meta').text(meta) | |
| 1150 | + } | |
| 1151 | + | |
| 1152 | + /** | |
| 1153 | + * Render the terminal state: what landed, what did not. | |
| 1154 | + * | |
| 1155 | + * @param status | |
| 1156 | + * | |
| 1157 | + * @since 2.5.16 | |
| 1158 | + */ | |
| 1159 | + function renderImportResult(status) { | |
| 1160 | + const imported = Array.isArray(status.imported) ? status.imported : [] | |
| 1161 | + const failed = Array.isArray(status.failed) ? status.failed : [] | |
| 1162 | + | |
| 1163 | + $('.wpfnl-import-progress').hide() | |
| 1164 | + $('.wpfnl-import-result').show() | |
| 1165 | + | |
| 1166 | + let summary = status.message || '' | |
| 1167 | + if (!summary) { | |
| 1168 | + summary = | |
| 1169 | + 'failed' === status.state | |
| 1170 | + ? importText('import_failed', 'The import could not be completed.') | |
| 1171 | + : importText('import_completed', 'Import complete.') | |
| 1172 | + } | |
| 1173 | + $('.wpfnl-import-result__summary').text(summary) | |
| 1174 | + | |
| 1175 | + const $list = $('.wpfnl-import-result__list').empty() | |
| 1176 | + | |
| 1177 | + imported.forEach(function (item) { | |
| 1178 | + const stepsWord = importText('import_steps_word', 'steps') | |
| 1179 | + let note = item.steps + ' ' + stepsWord | |
| 1180 | + | |
| 1181 | + // A partially imported funnel is the case worth being precise about: say how | |
| 1182 | + // many steps are missing rather than just flagging it as odd. | |
| 1183 | + if ('partial' === item.status) { | |
| 1184 | + const total = (item.steps || 0) + (item.failed_steps || 0) | |
| 1185 | + note = | |
| 1186 | + item.steps + | |
| 1187 | + ' / ' + | |
| 1188 | + total + | |
| 1189 | + ' ' + | |
| 1190 | + stepsWord + | |
| 1191 | + ' · ' + | |
| 1192 | + importText('import_partial', 'some steps failed') | |
| 1193 | + } | |
| 1194 | + | |
| 1195 | + $list.append( | |
| 1196 | + $('<li/>') | |
| 1197 | + .addClass('partial' === item.status ? 'is-failed' : '') | |
| 1198 | + .append($('<span/>').text(item.name || '')) | |
| 1199 | + .append($('<span/>').addClass('wpfnl-import-result__note').text(note)), | |
| 1200 | + ) | |
| 1201 | + }) | |
| 1202 | + | |
| 1203 | + failed.forEach(function (item) { | |
| 1204 | + $list.append( | |
| 1205 | + $('<li/>') | |
| 1206 | + .addClass('is-failed') | |
| 1207 | + .append($('<span/>').text(item.name || '')) | |
| 1208 | + .append( | |
| 1209 | + $('<span/>') | |
| 1210 | + .addClass('wpfnl-import-result__note') | |
| 1211 | + .text(item.message || importText('import_failed_item', 'failed')), | |
| 1212 | + ), | |
| 1213 | + ) | |
| 1214 | + }) | |
| 1215 | + | |
| 1216 | + // Only claim success when nothing failed. A partial run has already been spelled | |
| 1217 | + // out in the list above, and a cheerful toast on top of it would misrepresent it. | |
| 1218 | + if (imported.length && !failed.length) { | |
| 1219 | + try { | |
| 1220 | + localStorage.setItem('wpfnl_show_toast', 'import_success') | |
| 1221 | + } catch (e) { | |
| 1222 | + // Private mode / blocked storage: the result card already says what happened. | |
| 1223 | + } | |
| 1224 | + } | |
| 1225 | + } | |
| 1226 | + | |
| 1227 | + /** | |
| 1228 | + * Follow a background import to completion. | |
| 1229 | + * | |
| 1230 | + * Bounded polling with backoff rather than setInterval: the job can legitimately run | |
| 1231 | + * for minutes, and a fixed short interval would hammer the REST route for the whole | |
| 1232 | + * duration. Stall detection happens server-side — the process that died cannot report | |
| 1233 | + * on itself — so this only has to react to the state it is handed. | |
| 1234 | + * | |
| 1235 | + * @param runId | |
| 1236 | + * | |
| 1237 | + * @since 2.5.16 | |
| 1238 | + */ | |
| 1239 | + async function pollImportStatus(runId) { | |
| 1240 | + const started = Date.now() | |
| 1241 | + const maxMs = 15 * 60 * 1000 | |
| 1242 | + let attempt = 0 | |
| 1243 | + let consecutiveErrors = 0 | |
| 1244 | + | |
| 1245 | + while (Date.now() - started < maxMs) { | |
| 1246 | + const delay = attempt < 5 ? 1000 : attempt < 20 ? 2000 : 5000 | |
| 1247 | + await new Promise(function (resolve) { | |
| 1248 | + setTimeout(resolve, delay) | |
| 1249 | + }) | |
| 1250 | + attempt++ | |
| 1251 | + | |
| 1252 | + let status | |
| 1253 | + try { | |
| 1254 | + status = await fetchImportStatus(runId) | |
| 1255 | + consecutiveErrors = 0 | |
| 1256 | + } catch (e) { | |
| 1257 | + // A blip should not kill a long-running import view; three in a row means | |
| 1258 | + // something is actually wrong. | |
| 1259 | + consecutiveErrors++ | |
| 1260 | + if (consecutiveErrors >= 3) { | |
| 1261 | + renderImportResult({ | |
| 1262 | + state: 'failed', | |
| 1263 | + message: importText( | |
| 1264 | + 'import_status_unavailable', | |
| 1265 | + 'Lost contact with the import. Refresh the page to check on it.', | |
| 1266 | + ), | |
| 1267 | + }) | |
| 1268 | + return | |
| 1269 | + } | |
| 1270 | + continue | |
| 1271 | + } | |
| 1272 | + | |
| 1273 | + if (!runId && status.run_id) { | |
| 1274 | + runId = status.run_id | |
| 1275 | + } | |
| 1276 | + | |
| 1277 | + // Somebody else started an import; this view is no longer authoritative. | |
| 1278 | + if ('superseded' === status.state || 'idle' === status.state) { | |
| 1279 | + return | |
| 1280 | + } | |
| 1281 | + | |
| 1282 | + if ('stalled' === status.state) { | |
| 1283 | + $('.wpfnl-import-progress').addClass('is-stalled') | |
| 1284 | + $('.wpfnl-import-progress__label').text( | |
| 1285 | + importText( | |
| 1286 | + 'import_stalled', | |
| 1287 | + 'The import seems to have stopped responding. Cancel it to try again.', | |
| 1288 | + ), | |
| 1289 | + ) | |
| 1290 | + return | |
| 1291 | + } | |
| 1292 | + | |
| 1293 | + renderImportProgress(status) | |
| 1294 | + | |
| 1295 | + if ('completed' === status.state || 'failed' === status.state) { | |
| 1296 | + renderImportResult(status) | |
| 1297 | + return | |
| 1298 | + } | |
| 1299 | + } | |
| 1300 | + | |
| 1301 | + $('.wpfnl-import-progress__label').text( | |
| 1302 | + importText( | |
| 1303 | + 'import_taking_long', | |
| 1304 | + 'This import is taking longer than expected. Refresh the page to check on it.', | |
| 1305 | + ), | |
| 1306 | + ) | |
| 1307 | + } | |
| 1308 | + | |
| 1309 | + /** | |
| 1310 | + * Resume the progress view for an import still running from an earlier page view. | |
| 1311 | + * | |
| 1312 | + * @since 2.5.16 | |
| 1313 | + */ | |
| 1314 | + function resumeImportIfRunning() { | |
| 1315 | + if (!$('.import-funnel-modal').length || !window.WPFunnelVars) { | |
| 1316 | + return | |
| 1317 | + } | |
| 1318 | + | |
| 1319 | + fetchImportStatus('') | |
| 1320 | + .then(function (status) { | |
| 1321 | + if ('running' === status.state || 'queued' === status.state) { | |
| 1322 | + showImportProgress() | |
| 1323 | + renderImportProgress(status) | |
| 1324 | + pollImportStatus(status.run_id || '') | |
| 1325 | + } | |
| 1326 | + }) | |
| 1327 | + .catch(function () { | |
| 1328 | + // No import to resume, or the route is unreachable — stay quiet. | |
| 1329 | + }) | |
| 1330 | + } | |
| 1331 | + | |
| 1332 | + /** | |
| 1333 | + * Clear a stuck import so a new one can be started. | |
| 1334 | + * | |
| 1335 | + * @param event | |
| 1336 | + * | |
| 1337 | + * @since 2.5.16 | |
| 1338 | + */ | |
| 1339 | + FunnelHandler.prototype.CancelImport = function (event) { | |
| 1340 | + event.preventDefault() | |
| 1341 | + | |
| 1342 | + const $button = $(this) | |
| 1343 | + $button.find('.wpfnl-loader').css('display', 'inline-block') | |
| 1344 | + | |
| 1345 | + fetch(WPFunnelVars.rest_api_url + 'wpfunnels/v1/import-export/import-reset', { | |
| 1346 | + method: 'POST', | |
| 1347 | + headers: { | |
| 1348 | + 'Content-Type': 'application/json', | |
| 1349 | + 'X-WP-Nonce': WPFunnelVars.nonce, | |
| 1350 | + }, | |
| 1351 | + }) | |
| 1352 | + .then(function () { | |
| 1353 | + $button.find('.wpfnl-loader').css('display', 'none') | |
| 1354 | + $('.wpfnl-import-progress').hide().removeClass('is-stalled') | |
| 1355 | + $('.import-funnel-modal').removeClass('show-modal') | |
| 1356 | + $('#wpfnl-import-funnels').show() | |
| 1357 | + showToast( | |
| 1358 | + 'success', | |
| 1359 | + importText('import_cancelled', 'The import has been cancelled.'), | |
| 1360 | + ) | |
| 1361 | + }) | |
| 1362 | + .catch(function () { | |
| 1363 | + $button.find('.wpfnl-loader').css('display', 'none') | |
| 1364 | + showToast( | |
| 1365 | + 'error', | |
| 1366 | + importText('import_cancel_failed', 'Could not cancel the import.'), | |
| 1367 | + ) | |
| 1368 | + }) | |
| 1369 | + } | |
| 1370 | + | |
| 1371 | + /** | |
| 982 | 1372 | * Download json file |
| 983 | 1373 | * |
| 984 | 1374 | * @param content |
| 985 | 1375 | * @param fileName |
| @@ -1989,34 +2379,10 @@ | ||
| 1989 | 2379 | } |
| 1990 | 2380 | } |
| 1991 | 2381 | } |
| 1992 | 2382 | |
| 1993 | - /** | |
| 1994 | - * settings page tab nav | |
| 1995 | - * | |
| 1996 | - * @since 1.0.0 | |
| 1997 | - */ | |
| 1998 | - $('.wpfn-settings__nav .nav-li:not(.disabled)').on('click', function (e) { | |
| 1999 | - var dataID = $(this).attr('data-id'); | |
| 2000 | - | |
| 2001 | - $(this).addClass('active').siblings('.nav-li').removeClass('active'); | |
| 2002 | 2383 | |
| 2003 | - // Show the selected panel, hide others | |
| 2004 | - $('#' + dataID).show().siblings('.wpfnl-funnel__single-settings').hide(); | |
| 2005 | 2384 | |
| 2006 | - // Inside the visible panel, activate the first inner-tab | |
| 2007 | - var $container = $('#' + dataID); | |
| 2008 | - $container.find('.inner-tab').removeClass('active'); | |
| 2009 | - $container.find('.wpfnl-tab-content').removeClass('active'); | |
| 2010 | - | |
| 2011 | - var $firstInnerTab = $container.find('.inner-tab').first(); | |
| 2012 | - var tabId = $firstInnerTab.data('tab'); | |
| 2013 | - | |
| 2014 | - $firstInnerTab.addClass('active'); | |
| 2015 | - $('#' + tabId).addClass('active'); | |
| 2016 | - }); | |
| 2017 | - | |
| 2018 | - | |
| 2019 | 2385 | /** |
| 2020 | 2386 | * Checkout Edit Field delete alert modal |
| 2021 | 2387 | * |
| 2022 | 2388 | * @since 1.0.0 |
| @@ -2446,21 +2812,8 @@ | ||
| 2446 | 2812 | ) |
| 2447 | 2813 | } |
| 2448 | 2814 | }, |
| 2449 | 2815 | ) |
| 2450 | - | |
| 2451 | - /** | |
| 2452 | - * Funnel window page and Empty funnel page help resource show/hide. | |
| 2453 | - * | |
| 2454 | - */ | |
| 2455 | - $(document).on('click', '.wpfnl-helper-btn', function (e) { | |
| 2456 | - e.stopPropagation() | |
| 2457 | - $(this).parent('.wpfnl-canvas-helper').toggleClass('show-helpers') | |
| 2458 | - }) | |
| 2459 | - | |
| 2460 | - $(document).on('click', 'body', function () { | |
| 2461 | - $('.wpfnl-canvas-helper').removeClass('show-helpers') | |
| 2462 | - }) | |
| 2463 | 2816 | |
| 2464 | 2817 | /** |
| 2465 | 2818 | * add new step option dropdown show/hide. |
| 2466 | 2819 | * |