PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.9
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.9
4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 All 140 releases
← All changes | assets/src/js/admin/addons.js +425 -274 4.3.7 → 4.4.9 View file →
@@ -1,341 +1,492 @@
1 1 /**
2 - * Script handle admin notices.
2 + * Admin Add-ons page JS handler as a class.
3 3 *
4 - * @since 4.1.7.3.2
5 - * @version 1.0.1
4 + * Add-ons list is rendered server-side via AdminAddonsPage::render_addons()
5 + * through TemplateAJAX::load_content_via_ajax(). This class only handles
6 + * UI interactions and addon actions via window.lpAJAXG.
7 + *
8 + * @since 4.4.7
9 + * @version 1.0.0
6 10 */
7 -import API from '../api';
8 -let elAddonsPage;
9 -let dataHtml;
10 -let dataAddons;
11 -let elLPAddons;
12 -const queryString = window.location.search;
13 -const urlParams = new URLSearchParams( queryString );
14 -const tab = urlParams.get( 'tab' );
15 -let elNotifyActionWrapper;
16 -const isHandling = [];
11 +import * as lpToastify from 'lpAssetsJsPath/lpToastify.js';
12 +import * as lpUtils from 'lpAssetsJsPath/utils.js';
17 13
18 -// API get list addons.
19 -const getAddons = ( set = '' ) => {
20 - const params = tab ? `?tab=${ tab }` : `?${ set }`;
21 - fetch( API.admin.apiAddons + params, {
22 - method: 'GET',
23 - headers: {
24 - 'X-WP-Nonce': lpDataAdmin.nonce,
25 - },
26 - } ).then( ( res ) =>
27 - res.json()
28 - ).then( ( res ) => {
29 - // console.log(data);
30 - const { status, message, data } = res;
31 - if ( status === 'success' ) {
32 - dataHtml = data.html;
33 - dataAddons = data.addons;
34 - } else {
35 - dataHtml = message;
14 +class AdminAddons {
15 + constructor() {
16 + this.elAddonsPage = null;
17 + this.urlParams = new URLSearchParams( window.location.search );
18 + }
19 +
20 + static selectors = {
21 + elAddonsPage: '.lp-addons-page',
22 + elLPAddons: '#lp-addons',
23 + elAddonItem: '.lp-addon-item',
24 + elBtnAction: '.btn-addon-action',
25 + elNavTab: '.nav-tab',
26 + elNavTabActive: '.nav-tab.nav-tab-active',
27 + elCategory: '.lp-addons-category',
28 + elCategoryActive: '.lp-addons-category.active',
29 + elSearchInput: '#lp-search-addons__input',
30 + elItemPurchase: '.lp-addon-item__purchase',
31 + elPurchaseInstall: '.purchase-install',
32 + elPurchaseCode: '.enter-purchase-code',
33 + elLicense: '.lp-addon-license',
34 + };
35 +
36 + init() {
37 + // Content is injected via AJAX into .lp-target, wait for #lp-addons.
38 + lpUtils.lpOnElementReady( AdminAddons.selectors.elLPAddons, () => {
39 + this.elAddonsPage = document.querySelector(
40 + AdminAddons.selectors.elAddonsPage
41 + );
42 + if ( ! this.elAddonsPage ) {
43 + return;
44 + }
45 +
46 + this.filterAddons();
47 + this.events();
48 + } );
49 + }
50 +
51 + /**
52 + * Register events.
53 + *
54 + * @return void
55 + */
56 + events() {
57 + lpUtils.eventHandlers( 'click', [
58 + {
59 + selector: AdminAddons.selectors.elBtnAction,
60 + class: this,
61 + callBack: this.handleBtnAction.name,
62 + },
63 + {
64 + selector: AdminAddons.selectors.elNavTab,
65 + class: this,
66 + callBack: this.handleTabClick.name,
67 + },
68 + {
69 + selector: AdminAddons.selectors.elCategory,
70 + class: this,
71 + callBack: this.handleCategoryClick.name,
72 + },
73 + {
74 + selector: AdminAddons.selectors.elPurchaseCode,
75 + class: this,
76 + callBack: this.handlePurchaseCodeClick.name,
77 + },
78 + ] );
79 + lpUtils.eventHandlers( 'input', [
80 + {
81 + selector: AdminAddons.selectors.elSearchInput,
82 + class: this,
83 + callBack: this.handleSearchInput.name,
84 + },
85 + {
86 + selector: AdminAddons.selectors.elPurchaseCode,
87 + class: this,
88 + callBack: this.handlePurchaseCodeInput.name,
89 + },
90 + ] );
91 + }
92 +
93 + /**
94 + * Get addon data embedded on the item element.
95 + *
96 + * @param {Element} elAddonItem Addon item element.
97 + * @return {Object|null}
98 + */
99 + getAddonData( elAddonItem ) {
100 + try {
101 + return JSON.parse( elAddonItem.dataset.addon );
102 + } catch ( e ) {
103 + return null;
36 104 }
37 - } ).catch( ( err ) => {
38 - console.log( err );
39 - } );
40 -};
41 -// API send action install, update, activate, deactivate.
42 -const addonsAction = ( data, callBack ) => {
43 - const addonSlug = data.addon.slug;
105 + }
44 106
45 - if ( isHandling.indexOf( addonSlug ) !== -1 ) {
46 - return;
107 + /**
108 + * Send addon action to server via lpAJAXG.
109 + *
110 + * @param {Object} data { action, addon, purchase_code, el, elAddonItem }.
111 + * @return void
112 + */
113 + addonsAction( data ) {
114 + const { action, addon, el, elAddonItem } = data;
115 + const selectors = AdminAddons.selectors;
116 +
117 + const params = {
118 + action: 'addon_action',
119 + action_type: action,
120 + addon,
121 + purchase_code: data.purchase_code || '',
122 + id_url: 'addon-action',
123 + };
124 +
125 + window.lpAJAXG.fetchAJAX( params, {
126 + success: ( response ) => {
127 + const { status, message, data: resData } = response;
128 +
129 + lpToastify.show( message, status );
130 +
131 + if ( status === 'success' && resData?.html ) {
132 + elAddonItem.outerHTML = resData.html;
133 + } else if (
134 + 'install' === action ||
135 + 'update-purchase' === action
136 + ) {
137 + const elPurchasePanel = el.closest(
138 + selectors.elPurchaseInstall
139 + );
140 + const elPurchaseCode = elPurchasePanel
141 + ? elPurchasePanel.querySelector( selectors.elPurchaseCode )
142 + : null;
143 + if ( elPurchaseCode ) {
144 + elPurchaseCode.classList.add( 'is-error' );
145 + elPurchaseCode.setAttribute( 'aria-invalid', 'true' );
146 + }
147 + }
148 +
149 + //this.filterAddons();
150 + lpUtils.lpSetLoadingEl( el, 0 );
151 + },
152 + error: ( error ) => {
153 + lpUtils.lpSetLoadingEl( el, 0 );
154 + lpToastify.show( `error js: ${ error }`, 'error' );
155 + },
156 + } );
47 157 }
48 - isHandling.push( addonSlug );
49 158
50 - fetch( API.admin.apiAddonAction, {
51 - method: 'POST',
52 - headers: {
53 - 'Content-Type': 'application/json',
54 - 'X-WP-Nonce': lpDataAdmin.nonce,
55 - },
56 - body: JSON.stringify( { ...data } ),
57 - } ).then( ( res ) =>
58 - res.json()
59 - ).then( ( res ) => {
60 - const indexAddonHanding = isHandling.indexOf( addonSlug );
61 - if ( indexAddonHanding !== -1 ) {
62 - isHandling.splice( indexAddonHanding, 1 );
63 - }
159 + /**
160 + * Filter add-ons by status, category, and search query.
161 + *
162 + * @return void
163 + */
164 + filterAddons() {
165 + const selectors = AdminAddons.selectors;
166 + const elAddonItems = this.elAddonsPage.querySelectorAll(
167 + selectors.elAddonItem
168 + );
64 169
65 - const { status, message, data } = res;
170 + // Count items for each tab
171 + this.updateTabCounts( elAddonItems );
172 + const elActiveTab = this.elAddonsPage.querySelector(
173 + selectors.elNavTabActive
174 + );
175 + const elActiveCategory = this.elAddonsPage.querySelector(
176 + selectors.elCategoryActive
177 + );
178 + const elSearch = this.elAddonsPage.querySelector(
179 + selectors.elSearchInput
180 + );
181 + const tabName = elActiveTab ? elActiveTab.dataset.tab : 'all';
182 + const category = elActiveCategory
183 + ? elActiveCategory.dataset.category
184 + : 'all';
185 + const keyword = elSearch ? elSearch.value.trim().toLowerCase() : '';
186 + const searchTerms = keyword.split( /\s+/ ).filter( Boolean );
66 187
67 - if ( callBack ) {
68 - callBack( status, message, data );
69 - }
188 + // Count items for category by tab
189 + this.updateCategoryCounts( elAddonItems, tabName );
70 190
71 - handleNotify( status, message );
72 - } ).catch( ( err ) => {
73 - handleNotify( 'error', `error js: ${ err }` );
74 - console.log( err );
75 - } );
76 -};
77 -// Show notify.
78 -const handleNotify = ( status, message ) => {
79 - const elNotifyAction = elNotifyActionWrapper.querySelector( '.lp-notify-action' );
80 - const elNotifyActionNew = elNotifyAction.cloneNode( true );
81 - elNotifyActionNew.classList.remove( 'clone' );
82 - elNotifyActionWrapper.insertBefore( elNotifyActionNew, elNotifyActionWrapper[ 0 ] );
83 - const elSuccess = elNotifyActionNew.querySelector( `.${ elNotifyActionNew.classList.value }__success` );
84 - const elFailed = elNotifyActionNew.querySelector( `.${ elNotifyActionNew.classList.value }__error` );
191 + elAddonItems.forEach( ( elAddonItem ) => {
192 + const addonInfo = JSON.parse( elAddonItem.dataset.addon );
85 193
86 - if ( status === 'success' ) {
87 - elSuccess.classList.add( 'show' );
88 - elSuccess.querySelector( '.message' ).innerHTML = message;
89 - } else {
90 - elFailed.classList.add( 'show' );
91 - elFailed.querySelector( '.message' ).innerHTML = message;
194 + const addonName = elAddonItem
195 + .querySelector( 'a' )
196 + .textContent.toLowerCase();
197 + const textCompare = addonName + addonInfo.description;
198 +
199 + const addonCategories = ( elAddonItem.dataset.category || '' )
200 + .split( /\s+/ )
201 + .filter( Boolean );
202 + const matchesTab =
203 + 'all' === tabName || elAddonItem.classList.contains( tabName );
204 + const matchesCategory =
205 + 'all' === category || addonCategories.includes( category );
206 + const matchesSearch = searchTerms.every( ( term ) => textCompare.includes( term ) );
207 + const isVisible = matchesTab && matchesCategory && matchesSearch;
208 +
209 + elAddonItem.classList.toggle( 'hide', ! matchesTab );
210 + elAddonItem.classList.toggle(
211 + 'search-not-found',
212 + ! isVisible
213 + );
214 + } );
92 215 }
93 216
94 - elNotifyActionWrapper.classList.add( 'show' );
95 - setTimeout( () => {
96 - elNotifyActionNew.remove();
217 + updateCategoryCounts( elAddonItems, tabName ) {
218 + const categoryCounts = { all: 0 };
97 219
98 - const elNotifyAction = elNotifyActionWrapper.querySelectorAll( '.lp-notify-action' );
99 - if ( elNotifyAction.length === 1 ) {
100 - elNotifyActionWrapper.classList.remove( 'show' );
101 - }
102 - }, status === 'success' ? 3000 : 4000 );
103 -};
104 -// Get addons when js loaded.
105 -getAddons();
106 -// Search Addons.
107 -const searchAddons = ( name ) => {
108 - const elAddonItems = elAddonsPage.querySelectorAll( '.lp-addon-item' );
109 - let totalItems = 0;
220 + elAddonItems.forEach( ( elAddonItem ) => {
221 + const matchesTab =
222 + 'all' === tabName || elAddonItem.classList.contains( tabName );
110 223
111 - elAddonItems.forEach( ( elAddonItem ) => {
112 - const addonName = elAddonItem.querySelector( 'a' ).textContent;
113 - if ( elAddonItem.classList.contains( 'hide' ) ) {
114 - return;
115 - }
224 + if ( ! matchesTab ) {
225 + return;
226 + }
116 227
117 - if ( addonName.toLowerCase().includes( name.toLowerCase() ) ) {
118 - elAddonItem.classList.remove( 'search-not-found' );
119 - totalItems++;
120 - } else {
121 - elAddonItem.classList.add( 'search-not-found' );
122 - }
123 - } );
228 + categoryCounts.all++;
229 + ( elAddonItem.dataset.category || '' )
230 + .split( /\s+/ )
231 + .filter( Boolean )
232 + .forEach( ( addonCategory ) => {
233 + categoryCounts[ addonCategory ] =
234 + ( categoryCounts[ addonCategory ] || 0 ) + 1;
235 + } );
236 + } );
124 237
125 - setGridItems( totalItems );
126 -};
127 -// Set grid style items.
128 -const setGridItems = ( totalItems ) => {
129 - if ( totalItems < 4 ) {
130 - elLPAddons.classList.add( 'max-3-items' );
131 - } else {
132 - elLPAddons.classList.remove( 'max-3-items' );
238 + this.elAddonsPage
239 + .querySelectorAll( AdminAddons.selectors.elCategory )
240 + .forEach( ( elCategory ) => {
241 + const count =
242 + categoryCounts[ elCategory.dataset.category ] || 0;
243 + const elCount = elCategory.querySelector(
244 + '.lp-addons-category__count'
245 + );
246 +
247 + if ( elCount ) {
248 + elCount.textContent = `(${ count })`;
249 + }
250 +
251 + elCategory.hidden =
252 + 'all' !== elCategory.dataset.category && 0 === count;
253 + } );
133 254 }
134 -};
135 -// Check element loaded and data API returned.
136 -const loadElData = setInterval( () => {
137 - if ( ! elAddonsPage || ! elNotifyActionWrapper ) {
138 - elAddonsPage = document.querySelector( '.lp-addons-page' );
139 - elNotifyActionWrapper = document.querySelector( '.lp-notify-action-wrapper' );
140 - } else if ( dataHtml && elAddonsPage && elNotifyActionWrapper ) {
141 - elAddonsPage.innerHTML = dataHtml;
142 - elLPAddons = elAddonsPage.querySelector( '#lp-addons' );
143 - const elNavTabWrapper = document.querySelector( '.lp-nav-tab-wrapper' );
144 - if ( ! elNavTabWrapper ) {
145 - clearInterval( loadElData );
146 - return;
147 - }
148 255
149 - const elNavTabWrapperClone = elNavTabWrapper.cloneNode( true );
150 - elAddonsPage.insertBefore( elNavTabWrapperClone, elAddonsPage.children[ 0 ] );
151 - elNavTabWrapperClone.style.display = 'flex';
152 - elNavTabWrapper.remove();
153 - const elNavActive = elNavTabWrapperClone.querySelector( '.nav-tab.nav-tab-active span' );
154 - setGridItems( parseInt( elNavActive.textContent ) );
256 + updateTabCounts( elAddonItems ) {
257 + this.elAddonsPage
258 + .querySelectorAll( AdminAddons.selectors.elNavTab )
259 + .forEach( ( elNavTab ) => {
260 + const tabName = elNavTab.dataset.tab;
261 + const count = Array.from( elAddonItems ).filter(
262 + ( elAddonItem ) =>
263 + 'all' === tabName ||
264 + elAddonItem.classList.contains( tabName )
265 + ).length;
266 + const elCount = elNavTab.querySelector( 'span' );
155 267
156 - clearInterval( loadElData );
268 + if ( elCount ) {
269 + elCount.textContent = count;
270 + }
271 + } );
157 272 }
158 -}, 1 );
159 273
160 -document.addEventListener( 'DOMContentLoaded', ( e ) => {
161 -} );
274 + /**
275 + * Handle click events.
276 + *
277 + * @param {Object} args Click event arguments.
278 + * @return void
279 + */
280 + handlePurchaseCodeClick( args ) {
281 + const { target: el } = args;
162 282
163 -/*** Events ***/
164 -document.addEventListener( 'click', ( e ) => {
165 - let el = e.target;
166 - const tagName = el.tagName.toLowerCase();
167 - if ( tagName === 'span' ) {
168 - e.preventDefault();
169 - const elBtnAction = el.closest( '.btn-addon-action' );
170 - if ( elBtnAction ) {
171 - elBtnAction.click();
283 + if ( ! el.value.startsWith( '***' ) ) {
284 + return;
172 285 }
286 +
287 + el.value = '';
288 + const elItemPurchase = el.closest(
289 + AdminAddons.selectors.elItemPurchase
290 + );
291 + if ( elItemPurchase ) {
292 + elItemPurchase.querySelector( 'input[name=purchase-code]' ).value = '';
293 + }
173 294 }
174 295
175 - // Events actions: activate, deactivate.
176 - /*if ( el.classList.contains( 'lp-toggle-switch-label' ) ) {
177 - //e.preventDefault();
296 + /**
297 + * Handle addon action button click.
298 + *
299 + * @param {Object} args Click event arguments.
300 + * @return void
301 + */
302 + handleBtnAction( args ) {
303 + const { e, target } = args;
304 + const selectors = AdminAddons.selectors;
305 + const el = target.closest( selectors.elBtnAction );
178 306
179 - const elAddonItem = el.closest( '.lp-addon-item' );
180 - const idLabel = el.getAttribute( 'for' );
181 - const elInput = document.querySelector( `#${ idLabel }` );
182 - const action = elInput.getAttribute( 'data-action' );
183 - const addon = dataAddons[ elAddonItem.dataset.slug ];
184 - const addonSlug = addon.slug;
185 - const parent = el.closest( '.lp-toggle-switch' );
186 - const label = parent.querySelector( `label[for=${ idLabel }]` );
187 - const dashicons = parent.querySelector( '.dashicons-update' );
188 - dashicons.style.display = 'inline-block';
189 - label.style.display = 'none';
190 - const data = { action, addon };
191 - addonsAction( data, function( status, message, data ) {
192 - const elAddon = document.querySelector( `#${ addonSlug }` );
193 - if ( elAddon ) {
194 - const parent = elAddon.closest( '.lp-toggle-switch' );
195 - if ( parent ) {
196 - const dashicons = parent.querySelector( '.dashicons-update' );
197 - dashicons.style.display = 'none';
198 - if ( action === 'deactivate' ) {
199 - elAddon.setAttribute( 'data-action', 'activate' );
200 - } else if ( action === 'activate' ) {
201 - elAddon.setAttribute( 'data-action', 'deactivate' );
202 - }
203 - const label = parent.querySelector( `label[for=${ addonSlug }]` );
204 - label.style.display = 'inline-flex';
205 - }
206 - }
307 + // Ignore button type link
308 + if ( 'A' === el.tagName ) {
309 + return;
310 + }
207 311
208 - if ( status === 'success' ) {
209 - if ( action === 'deactivate' ) {
210 - elAddonItem.classList.remove( 'activated' );
211 - }
212 - if ( action === 'activate' ) {
213 - elAddonItem.classList.add( 'activated' );
214 - }
215 - }
216 - } );
217 - }*/
312 + e.preventDefault();
313 + lpUtils.lpSetLoadingEl( el, 1 );
218 314
219 - // Events actions: install, update, delete.
220 - if ( el.closest( '.btn-addon-action' ) ) {
221 - el = el.closest( '.btn-addon-action' );
222 - e.preventDefault();
223 - el.classList.add( 'handling' );
224 315 let purchaseCode = '';
225 - const elAddonItem = el.closest( '.lp-addon-item' );
226 - const addon = dataAddons[ elAddonItem.dataset.slug ];
316 + const elAddonItem = el.closest( selectors.elAddonItem );
317 + const addon = this.getAddonData( elAddonItem );
227 318 const action = el.dataset.action;
228 - const elItemPurchase = elAddonItem.querySelector( '.lp-addon-item__purchase' );
229 - //const elToggleSwitchInput = elAddonItem.querySelector( '.lp-toggle-switch-input' );
319 + const elItemPurchase = elAddonItem.querySelector(
320 + selectors.elItemPurchase
321 + );
230 322
231 - if ( action === 'purchase' ) {
232 - elItemPurchase.style.display = 'block';
233 - elItemPurchase.querySelector( '.purchase-install' ).style.display = 'flex';
323 + if ( ! addon ) {
324 + lpUtils.lpSetLoadingEl( el, 0 );
234 325 return;
235 - } else if ( action === 'update-purchase-code' ) {
236 - elItemPurchase.querySelector( '.purchase-update' ).style.display = 'flex';
326 + }
327 +
328 + if ( action === 'purchase' || action === 'update-purchase-code' ) {
329 + const elPurchaseInstall = elItemPurchase.querySelector(
330 + selectors.elPurchaseInstall
331 + );
332 + const elPurchaseCode = elPurchaseInstall.querySelector(
333 + selectors.elPurchaseCode
334 + );
335 + const elPurchaseSubmit = elPurchaseInstall.querySelector(
336 + '.lp-addon-purchase__submit'
337 + );
338 +
339 + if ( action === 'update-purchase-code' ) {
340 + const elLicense = elAddonItem.querySelector( selectors.elLicense );
341 + elPurchaseCode.value = elLicense
342 + ? elLicense.dataset.purchaseCodeMasked || ''
343 + : '';
344 + elPurchaseSubmit.dataset.action = 'update-purchase';
345 + } else {
346 + elPurchaseCode.value = '';
347 + elPurchaseSubmit.dataset.action = 'install';
348 + }
349 +
350 + elPurchaseCode.classList.remove( 'is-error' );
351 + elPurchaseCode.removeAttribute( 'aria-invalid' );
352 + elItemPurchase.querySelector( 'input[name=purchase-code]' ).value =
353 + '';
354 + elPurchaseInstall.style.display = 'flex';
237 355 elItemPurchase.style.display = 'block';
356 + elPurchaseCode.focus();
357 + lpUtils.lpSetLoadingEl( el, 0 );
238 358 return;
239 - } else if ( action === 'buy' ) {
240 - const link = el.dataset.link;
241 - window.open( link, '_blank' );
242 - return;
243 359 } else if ( action === 'cancel' ) {
244 360 elItemPurchase.style.display = 'none';
361 + elItemPurchase.querySelector(
362 + selectors.elPurchaseInstall
363 + ).style.display = 'none';
364 + lpUtils.lpSetLoadingEl( el, 0 );
245 365 return;
246 - } else if ( action === 'install' ) {
247 - if ( el.dataset.link ) {
248 - el.classList.remove( 'handling' );
249 - const link = el.dataset.link;
250 - window.open( link, '_blank' );
251 - return;
252 - }
253 366 }
254 367
255 368 // Send request to server.
256 369 if ( elItemPurchase ) {
257 - purchaseCode = elItemPurchase.querySelector( 'input[name=purchase-code]' ).value;
370 + purchaseCode = elItemPurchase.querySelector(
371 + 'input[name=purchase-code]'
372 + ).value;
258 373 }
259 374
260 - const data = { purchase_code: purchaseCode, action, addon };
261 - addonsAction( data, function( status, message, data ) {
262 - if ( status === 'success' ) {
263 - if ( action === 'install' ) {
264 - elAddonItem.classList.add( 'installed', 'activated' );
265 - elAddonItem.classList.remove( 'not_installed' );
266 - elItemPurchase.style.display = 'none';
267 - /*elToggleSwitchInput.setAttribute( 'checked', 'checked' );
268 - elToggleSwitchInput.setAttribute( 'data-action', 'deactivate' );*/
269 - const elNavInstalled = document.querySelector( '.nav-tab[data-tab=installed] span' );
270 - elNavInstalled.textContent = parseInt( elNavInstalled.textContent ) + 1;
271 - const elNavNoInstalled = document.querySelector( '.nav-tab[data-tab=not_installed] span' );
272 - elNavNoInstalled.textContent = parseInt( elNavNoInstalled.textContent ) - 1;
273 - elItemPurchase.querySelector( '.purchase-install' ).style.display = 'none';
274 - elItemPurchase.querySelector( '.purchase-update' ).querySelector( '.enter-purchase-code' ).value = purchaseCode;
275 - } else if ( action === 'update' ) {
276 - const elAddonVersionCurrent = elAddonItem.querySelector( '.addon-version-current' );
277 - elAddonVersionCurrent.innerHTML = addon.version;
278 - elAddonItem.classList.remove( 'update' );
279 - } else if ( action === 'activate' ) {
280 - elAddonItem.classList.add( 'activated' );
281 - } else if ( action === 'deactivate' ) {
282 - elAddonItem.classList.remove( 'activated' );
283 - } else if ( action === 'update-purchase' ) {
284 - elItemPurchase.style.display = 'none';
285 - }
286 - }
287 -
288 - el.classList.remove( 'handling' );
375 + this.addonsAction( {
376 + purchase_code: purchaseCode,
377 + action,
378 + addon,
379 + el,
380 + elAddonItem,
289 381 } );
290 382 }
291 383
292 - if ( el.classList.contains( 'nav-tab' ) ) {
384 + /**
385 + * Handle tab click.
386 + *
387 + * @param {Object} args Click event arguments.
388 + * @return void
389 + */
390 + handleTabClick( args ) {
391 + const { e, target } = args;
392 + const selectors = AdminAddons.selectors;
393 + const el = target.closest( selectors.elNavTab );
293 394 e.preventDefault();
294 - const elTabs = document.querySelectorAll( '.nav-tab' );
295 - elTabs.forEach( function( elTab ) {
395 +
396 + const elTabs = this.elAddonsPage.querySelectorAll( selectors.elNavTab );
397 + elTabs.forEach( ( elTab ) => {
296 398 elTab.classList.remove( 'nav-tab-active' );
399 + elTab.setAttribute( 'aria-pressed', 'false' );
297 400 } );
298 401 el.classList.add( 'nav-tab-active' );
402 + el.setAttribute( 'aria-pressed', 'true' );
403 +
299 404 const tabName = el.dataset.tab;
300 - const elAddonItems = elAddonsPage.querySelectorAll( '.lp-addon-item' );
301 - const elSearch = elAddonsPage.querySelector( '#lp-search-addons__input' );
405 + const elSearch = this.elAddonsPage.querySelector(
406 + selectors.elSearchInput
407 + );
302 408 elSearch.value = '';
409 + this.elAddonsPage
410 + .querySelectorAll( selectors.elCategory )
411 + .forEach( ( elCategory ) => {
412 + const isActive = 'all' === elCategory.dataset.category;
413 + elCategory.classList.toggle( 'active', isActive );
414 + elCategory.setAttribute(
415 + 'aria-pressed',
416 + isActive ? 'true' : 'false'
417 + );
418 + } );
303 419
304 - urlParams.set( 'tab', tabName );
305 - window.history.pushState( {}, '', `${ window.location.pathname }?${ urlParams.toString() }` );
306 - let totalItems = 0;
307 - elAddonItems.forEach( ( elAddonItem ) => {
308 - elAddonItem.classList.remove( 'search-not-found' );
309 - if ( 'all' === tabName || elAddonItem.classList.contains( tabName ) ) {
310 - elAddonItem.classList.remove( 'hide' );
311 - totalItems++;
312 - } else {
313 - elAddonItem.classList.add( 'hide' );
314 - }
315 - } );
420 + this.urlParams.set( 'tab', tabName );
421 + window.history.pushState(
422 + {},
423 + '',
424 + `${ window.location.pathname }?${ this.urlParams.toString() }`
425 + );
426 + this.filterAddons();
427 + }
316 428
317 - setGridItems( totalItems );
429 + /**
430 + * Handle category click.
431 + *
432 + * @param {Object} args Click event arguments.
433 + * @return void
434 + */
435 + handleCategoryClick( args ) {
436 + const { e, target } = args;
437 + const elCategory = target.closest(
438 + AdminAddons.selectors.elCategory
439 + );
440 + e.preventDefault();
441 +
442 + this.elAddonsPage
443 + .querySelectorAll( AdminAddons.selectors.elCategory )
444 + .forEach( ( item ) => {
445 + const isActive = item === elCategory;
446 + item.classList.toggle( 'active', isActive );
447 + item.setAttribute(
448 + 'aria-pressed',
449 + isActive ? 'true' : 'false'
450 + );
451 + } );
452 + this.filterAddons();
318 453 }
319 -} );
320 454
321 -/*** Event search addons. ***/
322 -document.addEventListener( 'input', ( e ) => {
323 - const el = e.target;
324 -
325 - if ( 'lp-search-addons__input' === el.id ) {
326 - const keyword = el.value;
327 - searchAddons( keyword );
455 + /**
456 + * Handle search input.
457 + *
458 + * @return void
459 + */
460 + handleSearchInput() {
461 + this.filterAddons();
328 462 }
329 463
330 - // Events change input purchase code.
331 - if ( el.classList.contains( 'enter-purchase-code' ) ) {
464 + /**
465 + * Handle purchase code input.
466 + *
467 + * @param {Object} args Input event arguments.
468 + * @return void
469 + */
470 + handlePurchaseCodeInput( args ) {
471 + const { e, target: el } = args;
332 472 e.preventDefault();
473 + el.classList.remove( 'is-error' );
474 + el.removeAttribute( 'aria-invalid' );
333 475 const purchaseCode = el.value;
334 - const elItemPurchase = el.closest( '.lp-addon-item__purchase' );
476 + const elItemPurchase = el.closest(
477 + AdminAddons.selectors.elItemPurchase
478 + );
335 479
336 480 if ( elItemPurchase ) {
337 - const input = elItemPurchase.querySelector( 'input[name=purchase-code]' );
481 + const input = elItemPurchase.querySelector(
482 + 'input[name=purchase-code]'
483 + );
338 484 input.value = purchaseCode;
339 485 }
340 486 }
341 -} );
487 +}
488 +
489 +const adminAddons = new AdminAddons();
490 +adminAddons.init();
491 +
492 +export default AdminAddons;