2022-05-26 20:56:22 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2023-03-14 16:16:19 +00:00
|
|
|
import { addCustomerEffortScoreExitPageListener } from '@woocommerce/customer-effort-score';
|
2022-05-26 20:56:22 +00:00
|
|
|
import { recordEvent } from '@woocommerce/tracks';
|
|
|
|
|
2022-05-30 13:06:34 +00:00
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
2023-04-12 19:37:23 +00:00
|
|
|
import {
|
|
|
|
attachEventListenerToParentForChildren,
|
|
|
|
waitUntilElementIsPresent,
|
|
|
|
} from './utils';
|
2022-05-30 13:06:34 +00:00
|
|
|
|
2022-05-26 20:56:22 +00:00
|
|
|
/**
|
|
|
|
* Get the product data.
|
|
|
|
*
|
|
|
|
* @return object
|
|
|
|
*/
|
2022-11-29 01:01:10 +00:00
|
|
|
|
|
|
|
const isElementVisible = ( element: HTMLElement ) =>
|
|
|
|
! ( window.getComputedStyle( element ).display === 'none' );
|
|
|
|
|
2023-03-14 22:16:01 +00:00
|
|
|
const getProductType = () => {
|
|
|
|
return ( document.querySelector( '#product-type' ) as HTMLInputElement )
|
|
|
|
?.value;
|
|
|
|
};
|
|
|
|
|
2023-05-02 17:42:57 +00:00
|
|
|
type ProductTypeOption = {
|
|
|
|
id: string | null;
|
|
|
|
isEnabled: boolean;
|
|
|
|
};
|
|
|
|
|
|
|
|
function getProductTypeOptions() {
|
|
|
|
const productTypeOptionsCheckboxes = Array.from(
|
|
|
|
document.querySelectorAll(
|
|
|
|
'input[type="checkbox"][data-product-type-option-id]'
|
|
|
|
)
|
|
|
|
) as HTMLInputElement[];
|
|
|
|
const productTypeOptions = productTypeOptionsCheckboxes.map(
|
|
|
|
( checkbox ) => {
|
|
|
|
return {
|
|
|
|
id: checkbox.getAttribute( 'data-product-type-option-id' ),
|
|
|
|
isEnabled: checkbox.checked,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
);
|
|
|
|
return productTypeOptions;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getProductTypeOptionsString(
|
|
|
|
productTypeOptions: ProductTypeOption[]
|
|
|
|
) {
|
|
|
|
return productTypeOptions
|
|
|
|
.filter( ( productTypeOption ) => productTypeOption.isEnabled )
|
|
|
|
.map( ( productTypeOption ) => productTypeOption.id )
|
|
|
|
.join( ', ' );
|
|
|
|
}
|
|
|
|
|
2024-05-27 17:42:15 +00:00
|
|
|
export const getProductData = () => {
|
2022-11-29 01:01:10 +00:00
|
|
|
const isBlockEditor =
|
|
|
|
document.querySelectorAll( '.block-editor' ).length > 0;
|
|
|
|
|
|
|
|
let description_value = '';
|
|
|
|
let tagsText = '';
|
|
|
|
|
|
|
|
if ( ! isBlockEditor ) {
|
|
|
|
tagsText = (
|
|
|
|
document.querySelector(
|
|
|
|
'[name="tax_input[product_tag]"]'
|
|
|
|
) as HTMLInputElement
|
|
|
|
).value;
|
|
|
|
const content = document.querySelector(
|
|
|
|
'#content'
|
|
|
|
) as HTMLInputElement;
|
|
|
|
if ( content && isElementVisible( content ) ) {
|
|
|
|
description_value = content.value;
|
|
|
|
} else if ( typeof tinymce === 'object' && tinymce.get( 'content' ) ) {
|
|
|
|
description_value = tinymce.get( 'content' ).getContent();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
description_value = (
|
|
|
|
document.querySelector(
|
|
|
|
'.block-editor-rich-text__editable'
|
|
|
|
) as HTMLInputElement
|
|
|
|
)?.value;
|
|
|
|
}
|
|
|
|
|
2023-05-02 17:42:57 +00:00
|
|
|
const productTypeOptions = getProductTypeOptions();
|
|
|
|
const productTypeOptionsString =
|
|
|
|
getProductTypeOptionsString( productTypeOptions );
|
|
|
|
|
2022-11-29 01:01:10 +00:00
|
|
|
const productData = {
|
2022-05-26 20:56:22 +00:00
|
|
|
product_id: ( document.querySelector( '#post_ID' ) as HTMLInputElement )
|
|
|
|
?.value,
|
2023-03-14 22:16:01 +00:00
|
|
|
product_type: getProductType(),
|
2023-05-02 17:42:57 +00:00
|
|
|
product_type_options: productTypeOptionsString,
|
2022-06-21 08:37:34 +00:00
|
|
|
is_downloadable: (
|
|
|
|
document.querySelector( '#_downloadable' ) as HTMLInputElement
|
2022-11-29 01:01:10 +00:00
|
|
|
)?.checked
|
|
|
|
? 'Yes'
|
|
|
|
: 'No',
|
2022-06-21 08:37:34 +00:00
|
|
|
is_virtual: (
|
|
|
|
document.querySelector( '#_virtual' ) as HTMLInputElement
|
2022-11-29 01:01:10 +00:00
|
|
|
)?.checked
|
|
|
|
? 'Yes'
|
|
|
|
: 'No',
|
2022-06-21 08:37:34 +00:00
|
|
|
manage_stock: (
|
|
|
|
document.querySelector( '#_manage_stock' ) as HTMLInputElement
|
2022-11-29 01:01:10 +00:00
|
|
|
)?.checked
|
|
|
|
? 'Yes'
|
|
|
|
: 'No',
|
|
|
|
attributes: document.querySelectorAll( '.woocommerce_attribute' )
|
|
|
|
.length,
|
|
|
|
categories: document.querySelectorAll(
|
|
|
|
'[name="tax_input[product_cat][]"]:checked'
|
|
|
|
).length,
|
|
|
|
cross_sells: document.querySelectorAll( '#crosssell_ids option' ).length
|
|
|
|
? 'Yes'
|
|
|
|
: 'No',
|
|
|
|
description: description_value.trim() !== '' ? 'Yes' : 'No',
|
|
|
|
enable_reviews: (
|
|
|
|
document.querySelector( '#comment_status' ) as HTMLInputElement
|
|
|
|
)?.checked
|
|
|
|
? 'Yes'
|
|
|
|
: 'No',
|
|
|
|
is_block_editor: isBlockEditor,
|
|
|
|
menu_order:
|
|
|
|
parseInt(
|
|
|
|
( document.querySelector( '#menu_order' ) as HTMLInputElement )
|
|
|
|
?.value ?? 0,
|
|
|
|
10
|
|
|
|
) !== 0
|
|
|
|
? 'Yes'
|
|
|
|
: 'No',
|
|
|
|
product_gallery: document.querySelectorAll(
|
|
|
|
'#product_images_container .product_images > li'
|
|
|
|
).length,
|
|
|
|
product_image:
|
|
|
|
parseInt(
|
|
|
|
(
|
|
|
|
document.querySelector(
|
|
|
|
'#_thumbnail_id'
|
|
|
|
) as HTMLInputElement
|
|
|
|
)?.value,
|
|
|
|
10
|
|
|
|
) > 0
|
|
|
|
? 'Yes'
|
|
|
|
: 'No',
|
|
|
|
purchase_note: (
|
|
|
|
document.querySelector( '#_purchase_note' ) as HTMLInputElement
|
|
|
|
)?.value.length
|
|
|
|
? 'Yes'
|
|
|
|
: 'No',
|
|
|
|
sale_price: (
|
|
|
|
document.querySelector( '#_sale_price' ) as HTMLInputElement
|
|
|
|
)?.value
|
|
|
|
? 'Yes'
|
|
|
|
: 'No',
|
|
|
|
short_description: (
|
|
|
|
document.querySelector( '#excerpt' ) as HTMLInputElement
|
|
|
|
)?.value.length
|
|
|
|
? 'Yes'
|
|
|
|
: 'No',
|
|
|
|
tags: tagsText.length > 0 ? tagsText.split( ',' ).length : 0,
|
|
|
|
upsells: document.querySelectorAll( '#upsell_ids option' ).length
|
|
|
|
? 'Yes'
|
|
|
|
: 'No',
|
|
|
|
weight: ( document.querySelector( '#_weight' ) as HTMLInputElement )
|
|
|
|
?.value
|
|
|
|
? 'Yes'
|
|
|
|
: 'No',
|
2022-05-26 20:56:22 +00:00
|
|
|
};
|
2022-11-29 01:01:10 +00:00
|
|
|
return productData;
|
2022-05-26 20:56:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the publish date as a string.
|
|
|
|
*
|
2023-08-29 22:40:45 +00:00
|
|
|
* @param prefix Prefix for date element selectors.
|
2022-05-26 20:56:22 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
const getPublishDate = ( prefix = '' ) => {
|
2022-06-21 08:37:34 +00:00
|
|
|
const month = (
|
|
|
|
document.querySelector( `#${ prefix }mm` ) as HTMLInputElement
|
|
|
|
)?.value;
|
|
|
|
const day = (
|
|
|
|
document.querySelector( `#${ prefix }jj` ) as HTMLInputElement
|
|
|
|
)?.value;
|
|
|
|
const year = (
|
|
|
|
document.querySelector( `#${ prefix }aa` ) as HTMLInputElement
|
|
|
|
)?.value;
|
|
|
|
const hours = (
|
|
|
|
document.querySelector( `#${ prefix }hh` ) as HTMLInputElement
|
|
|
|
)?.value;
|
|
|
|
const seconds = (
|
|
|
|
document.querySelector( `#${ prefix }mn` ) as HTMLInputElement
|
|
|
|
)?.value;
|
2022-05-26 20:56:22 +00:00
|
|
|
|
|
|
|
return `${ month }-${ day }-${ year } ${ hours }:${ seconds }`;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the data from the publishing widget.
|
|
|
|
*
|
|
|
|
* @return object
|
|
|
|
*/
|
|
|
|
const getPublishingWidgetData = () => {
|
|
|
|
return {
|
|
|
|
status: ( document.querySelector( '#post_status' ) as HTMLInputElement )
|
|
|
|
?.value,
|
2022-06-21 08:37:34 +00:00
|
|
|
visibility: (
|
|
|
|
document.querySelector(
|
|
|
|
'input[name="visibility"]:checked'
|
|
|
|
) as HTMLInputElement
|
|
|
|
)?.value,
|
2022-05-26 20:56:22 +00:00
|
|
|
date: getPublishDate() !== getPublishDate( 'hidden_' ) ? 'yes' : 'no',
|
2022-06-21 08:37:34 +00:00
|
|
|
catalog_visibility: (
|
|
|
|
document.querySelector(
|
|
|
|
'input[name="_visibility"]:checked'
|
|
|
|
) as HTMLInputElement
|
|
|
|
)?.value,
|
2022-05-26 20:56:22 +00:00
|
|
|
featured: ( document.querySelector( '#_featured' ) as HTMLInputElement )
|
|
|
|
?.checked,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prefix all object keys with a string.
|
|
|
|
*
|
2023-08-29 22:40:45 +00:00
|
|
|
* @param obj Object to create keys from.
|
|
|
|
* @param prefix Prefix used before all keys.
|
2022-05-26 20:56:22 +00:00
|
|
|
* @return object
|
|
|
|
*/
|
|
|
|
const prefixObjectKeys = (
|
|
|
|
obj: { [ key: string ]: unknown },
|
|
|
|
prefix: string
|
|
|
|
) => {
|
|
|
|
return Object.fromEntries(
|
|
|
|
Object.entries( obj ).map( ( [ k, v ] ) => [ `${ prefix }${ k }`, v ] )
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2023-03-14 22:16:01 +00:00
|
|
|
/**
|
|
|
|
* Gets the tab name for a tab element.
|
|
|
|
*
|
2023-08-29 22:40:45 +00:00
|
|
|
* @param tab Tab element to get slug for.
|
2023-03-14 22:16:01 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
const getTabName = ( tab: Element ) => {
|
|
|
|
const optionsSuffix = '_options';
|
|
|
|
|
|
|
|
const optionsClassNames = Array.from( tab.classList ).filter(
|
|
|
|
( className ) => className.endsWith( optionsSuffix )
|
|
|
|
);
|
|
|
|
|
|
|
|
if ( optionsClassNames.length > 0 ) {
|
|
|
|
const className = optionsClassNames[ 0 ];
|
|
|
|
|
|
|
|
return className.slice( 0, -optionsSuffix.length );
|
|
|
|
}
|
|
|
|
|
|
|
|
return '';
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets additional data associated with a product tab click.
|
|
|
|
*
|
2023-08-29 22:40:45 +00:00
|
|
|
* @param tabName The name of the tab to get data for.
|
2023-03-14 22:16:01 +00:00
|
|
|
* @return object
|
|
|
|
*/
|
|
|
|
const getDataForProductTabClickEvent = ( tabName: string ) => {
|
|
|
|
const data: Record< string, boolean | string > = {};
|
|
|
|
|
|
|
|
data.product_type = getProductType();
|
|
|
|
|
|
|
|
if ( tabName === 'inventory' ) {
|
|
|
|
data.is_store_stock_management_enabled =
|
|
|
|
document.querySelector( '#_manage_stock' ) !== null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2023-04-12 19:37:23 +00:00
|
|
|
* Attaches the product tabs Tracks events.
|
2023-03-14 22:16:01 +00:00
|
|
|
*/
|
2023-04-12 19:37:23 +00:00
|
|
|
const attachProductTabsTracks = () => {
|
2023-03-14 22:16:01 +00:00
|
|
|
const tabs = document.querySelectorAll( '.product_data_tabs > li' );
|
|
|
|
|
|
|
|
tabs.forEach( ( tab ) => {
|
|
|
|
const tabName = getTabName( tab );
|
|
|
|
|
|
|
|
tab.querySelector( 'a' )?.addEventListener( 'click', () => {
|
|
|
|
recordEvent( 'product_tab_click', {
|
|
|
|
product_tab: tabName,
|
|
|
|
...getDataForProductTabClickEvent( tabName ),
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2023-04-12 19:37:23 +00:00
|
|
|
* Attaches the inventory tab Tracks events.
|
2023-03-14 22:16:01 +00:00
|
|
|
*/
|
2023-04-12 19:37:23 +00:00
|
|
|
const attachProductInventoryTabTracks = () => {
|
2023-03-14 22:16:01 +00:00
|
|
|
document
|
|
|
|
.querySelector( '#_manage_stock' )
|
|
|
|
?.addEventListener( 'click', ( event ) => {
|
|
|
|
recordEvent( 'product_manage_stock_click', {
|
|
|
|
is_enabled: ( event.target as HTMLInputElement )?.checked,
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
document
|
2023-04-12 19:37:23 +00:00
|
|
|
.querySelector( '#_manage_stock_disabled > a' )
|
2023-03-14 22:16:01 +00:00
|
|
|
?.addEventListener( 'click', () => {
|
|
|
|
recordEvent(
|
|
|
|
'product_manage_stock_disabled_store_settings_link_click'
|
|
|
|
);
|
|
|
|
} );
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelector( '#inventory_product_data .notice a' )
|
|
|
|
?.addEventListener( 'click', () => {
|
|
|
|
recordEvent(
|
|
|
|
'product_inventory_variations_notice_learn_more_click'
|
|
|
|
);
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
2022-05-26 20:56:22 +00:00
|
|
|
/**
|
2023-04-12 19:37:23 +00:00
|
|
|
* Attaches product tags tracks.
|
2022-05-26 20:56:22 +00:00
|
|
|
*/
|
2023-04-12 19:37:23 +00:00
|
|
|
const attachProductTagsTracks = () => {
|
2023-03-01 22:36:38 +00:00
|
|
|
function deleteTagEventListener(/* event: Event */) {
|
2022-05-30 13:06:34 +00:00
|
|
|
recordEvent( 'product_tags_delete', {
|
|
|
|
page: 'product',
|
|
|
|
tag_list_size:
|
|
|
|
document.querySelector( '.tagchecklist' )?.children.length || 0,
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
function addTagsDeleteTracks() {
|
|
|
|
const tagsDeleteButtons = document.querySelectorAll(
|
|
|
|
'#product_tag .ntdelbutton'
|
|
|
|
);
|
|
|
|
tagsDeleteButtons.forEach( ( button ) => {
|
|
|
|
button.removeEventListener( 'click', deleteTagEventListener );
|
|
|
|
button.addEventListener( 'click', deleteTagEventListener );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
waitUntilElementIsPresent(
|
|
|
|
'#product_tag .tagchecklist',
|
|
|
|
addTagsDeleteTracks
|
|
|
|
);
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelector( '.tagadd' )
|
2023-03-01 22:36:38 +00:00
|
|
|
?.addEventListener( 'click', (/* event: Event */) => {
|
2022-05-30 13:06:34 +00:00
|
|
|
const tagInput = document.querySelector< HTMLInputElement >(
|
|
|
|
'#new-tag-product_tag'
|
|
|
|
);
|
|
|
|
if ( tagInput && tagInput.value && tagInput.value.length > 0 ) {
|
|
|
|
recordEvent( 'product_tags_add', {
|
|
|
|
page: 'product',
|
|
|
|
tag_string_length: tagInput.value.length,
|
|
|
|
tag_list_size:
|
|
|
|
( document.querySelector( '.tagchecklist' )?.children
|
|
|
|
.length || 0 ) + 1,
|
|
|
|
most_used: false,
|
|
|
|
} );
|
|
|
|
setTimeout( () => {
|
|
|
|
addTagsDeleteTracks();
|
|
|
|
}, 500 );
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
|
|
|
function addMostUsedTagEventListener( event: Event ) {
|
|
|
|
recordEvent( 'product_tags_add', {
|
|
|
|
page: 'product',
|
|
|
|
tag_string_length: ( event.target as HTMLAnchorElement ).textContent
|
|
|
|
?.length,
|
|
|
|
tag_list_size:
|
|
|
|
document.querySelector( '.tagchecklist' )?.children.length || 0,
|
|
|
|
most_used: true,
|
|
|
|
} );
|
|
|
|
addTagsDeleteTracks();
|
|
|
|
}
|
|
|
|
|
|
|
|
function addMostUsedTagsTracks() {
|
|
|
|
const tagCloudLinks = document.querySelectorAll(
|
|
|
|
'#tagcloud-product_tag .tag-cloud-link'
|
|
|
|
);
|
|
|
|
tagCloudLinks.forEach( ( button ) => {
|
|
|
|
button.removeEventListener( 'click', addMostUsedTagEventListener );
|
|
|
|
button.addEventListener( 'click', addMostUsedTagEventListener );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelector( '.tagcloud-link' )
|
|
|
|
?.addEventListener( 'click', () => {
|
|
|
|
waitUntilElementIsPresent(
|
|
|
|
'#tagcloud-product_tag',
|
|
|
|
addMostUsedTagsTracks
|
|
|
|
);
|
|
|
|
} );
|
2023-04-12 19:37:23 +00:00
|
|
|
};
|
2022-05-30 13:06:34 +00:00
|
|
|
|
2023-04-12 19:37:23 +00:00
|
|
|
/**
|
|
|
|
* Attaches attributes tracks.
|
|
|
|
*/
|
|
|
|
const attachAttributesTracks = () => {
|
2023-05-10 00:04:29 +00:00
|
|
|
attachEventListenerToParentForChildren( '#product_attributes', [
|
|
|
|
{
|
|
|
|
eventName: 'click',
|
|
|
|
childQuery: '.add_new_attribute',
|
|
|
|
callback: () => {
|
|
|
|
recordEvent( 'product_attributes_add_term', {
|
|
|
|
page: 'product',
|
|
|
|
} );
|
|
|
|
},
|
|
|
|
},
|
|
|
|
] );
|
2023-04-12 19:37:23 +00:00
|
|
|
};
|
2022-05-30 13:06:34 +00:00
|
|
|
|
2023-04-12 19:37:23 +00:00
|
|
|
/**
|
2023-05-10 00:04:29 +00:00
|
|
|
* Attaches Tracks event for when a new custom attribute is added to a product.
|
2023-04-12 19:37:23 +00:00
|
|
|
*/
|
2023-05-10 00:04:29 +00:00
|
|
|
const attachAddCustomAttributeTracks = () => {
|
2023-04-17 15:50:30 +00:00
|
|
|
document
|
|
|
|
.querySelector( '#product_attributes .add_custom_attribute' )
|
|
|
|
?.addEventListener( 'click', () => {
|
|
|
|
recordEvent( 'product_attributes_buttons', {
|
2023-05-10 00:04:29 +00:00
|
|
|
action: 'add_new',
|
2023-04-17 15:50:30 +00:00
|
|
|
} );
|
|
|
|
} );
|
2023-05-10 00:04:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attaches Tracks event for when an existing global attribute is added to a product.
|
|
|
|
*/
|
|
|
|
const attachAddExistingAttributeTracks = () => {
|
|
|
|
window
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
// @ts-ignore Need to use jQuery to hook up to the select2:select event since the select2 component is jQuery-based
|
|
|
|
?.jQuery( 'select.wc-attribute-search' )
|
|
|
|
.on( 'select2:select', function () {
|
|
|
|
recordEvent( 'product_attributes_buttons', {
|
|
|
|
action: 'add_existing',
|
|
|
|
} );
|
2023-04-17 15:50:30 +00:00
|
|
|
} );
|
2023-05-10 00:04:29 +00:00
|
|
|
};
|
|
|
|
|
2023-05-19 15:44:54 +00:00
|
|
|
/**
|
|
|
|
* Gets number of attributes with 'Used for variations' checked.
|
|
|
|
*/
|
|
|
|
const getUsedForVariationsAttributesCount = () =>
|
|
|
|
( document.querySelector( '#product-type' ) as HTMLSelectElement )
|
|
|
|
?.value === 'variable'
|
|
|
|
? document.querySelectorAll(
|
|
|
|
'input[name^="attribute_variation"]:checked'
|
|
|
|
).length
|
|
|
|
: 0;
|
|
|
|
|
2023-05-10 00:04:29 +00:00
|
|
|
/**
|
|
|
|
* Attaches product attributes tracks.
|
|
|
|
*/
|
|
|
|
const attachProductAttributesTracks = () => {
|
|
|
|
attachAddCustomAttributeTracks();
|
|
|
|
attachAddExistingAttributeTracks();
|
2023-04-17 15:50:30 +00:00
|
|
|
|
|
|
|
const attributesSection = '#product_attributes';
|
|
|
|
|
|
|
|
// We attach the events in this way because the buttons are added dynamically.
|
|
|
|
attachEventListenerToParentForChildren( attributesSection, [
|
|
|
|
{
|
|
|
|
eventName: 'click',
|
|
|
|
childQuery: '.woocommerce_attribute_visible_on_product_page',
|
|
|
|
callback: ( clickedElement ) => {
|
|
|
|
const elementName = clickedElement.getAttribute( 'name' );
|
|
|
|
const visibleOnProductPage = document.querySelector(
|
|
|
|
`[name="${ elementName }"]`
|
|
|
|
) as HTMLInputElement;
|
|
|
|
|
|
|
|
recordEvent( 'product_attributes_buttons', {
|
|
|
|
action: 'visible_on_product_page',
|
|
|
|
checked: visibleOnProductPage?.checked,
|
|
|
|
} );
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
eventName: 'click',
|
|
|
|
childQuery: '.woocommerce_attribute_used_for_variations',
|
|
|
|
callback: ( clickedElement ) => {
|
|
|
|
const elementName = clickedElement.getAttribute( 'name' );
|
|
|
|
const usedForVariations = document.querySelector(
|
|
|
|
`[name="${ elementName }"]`
|
|
|
|
) as HTMLInputElement;
|
|
|
|
|
|
|
|
recordEvent( 'product_attributes_buttons', {
|
|
|
|
action: 'used_for_variations',
|
|
|
|
checked: usedForVariations?.checked,
|
|
|
|
} );
|
|
|
|
},
|
|
|
|
},
|
|
|
|
] );
|
|
|
|
|
2022-05-30 13:06:34 +00:00
|
|
|
document
|
|
|
|
.querySelector( '.save_attributes' )
|
2023-04-05 19:22:59 +00:00
|
|
|
?.addEventListener( 'click', ( event ) => {
|
|
|
|
if (
|
|
|
|
event.target instanceof Element &&
|
|
|
|
event.target.classList.contains( 'disabled' )
|
|
|
|
) {
|
|
|
|
// skip in case the button is disabled
|
|
|
|
return;
|
|
|
|
}
|
2023-05-19 15:44:54 +00:00
|
|
|
|
|
|
|
const localAttributesCount = document.querySelectorAll(
|
|
|
|
'.woocommerce_attribute:not(.taxonomy)'
|
2022-05-30 13:06:34 +00:00
|
|
|
).length;
|
2023-05-19 15:44:54 +00:00
|
|
|
|
|
|
|
const globalAttributesCount = document.querySelectorAll(
|
|
|
|
'.woocommerce_attribute.taxonomy'
|
|
|
|
).length;
|
|
|
|
|
|
|
|
recordEvent( 'product_attributes_save', {
|
|
|
|
page: 'product',
|
|
|
|
total_attributes_count:
|
|
|
|
globalAttributesCount + localAttributesCount,
|
|
|
|
local_attributes_count: localAttributesCount,
|
|
|
|
global_attributes_count: globalAttributesCount,
|
|
|
|
visible_on_product_page_count: document.querySelectorAll(
|
|
|
|
'input[name^="attribute_visibility"]:checked'
|
|
|
|
).length,
|
|
|
|
used_for_variations_count:
|
|
|
|
getUsedForVariationsAttributesCount(),
|
|
|
|
} );
|
2022-05-30 13:06:34 +00:00
|
|
|
} );
|
2023-04-12 19:37:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attaches product variations tracks.
|
|
|
|
*/
|
|
|
|
const attachProductVariationsTracks = () => {
|
|
|
|
document
|
|
|
|
.querySelector(
|
|
|
|
'#variable_product_options_inner .variations-add-attributes-link'
|
|
|
|
)
|
|
|
|
?.addEventListener( 'click', () => {
|
|
|
|
recordEvent( 'product_variations_empty_state', {
|
|
|
|
action: 'add_attribute_link',
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelector(
|
|
|
|
'#variable_product_options_inner .variations-learn-more-link'
|
|
|
|
)
|
|
|
|
?.addEventListener( 'click', () => {
|
|
|
|
recordEvent( 'product_variations_empty_state', {
|
|
|
|
action: 'learn_more_link',
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
const variationsSection = '#variable_product_options';
|
|
|
|
|
|
|
|
// We attach the events in this way because the buttons are added dynamically.
|
|
|
|
attachEventListenerToParentForChildren( variationsSection, [
|
|
|
|
{
|
|
|
|
eventName: 'click',
|
|
|
|
childQuery: '.add_variation_manually',
|
|
|
|
callback: () => {
|
|
|
|
recordEvent( 'product_variations_buttons', {
|
|
|
|
action: 'add_variation_manually',
|
|
|
|
} );
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
eventName: 'change',
|
|
|
|
childQuery: '#field_to_edit',
|
|
|
|
callback: () => {
|
|
|
|
const selectElement = document.querySelector(
|
|
|
|
'#field_to_edit'
|
|
|
|
) as HTMLSelectElement;
|
|
|
|
// Get the index of the selected option
|
|
|
|
const selectedIndex = selectElement.selectedIndex;
|
|
|
|
recordEvent( 'product_variations_buttons', {
|
|
|
|
action: 'bulk_actions',
|
|
|
|
selected: selectElement.options[ selectedIndex ]?.value,
|
|
|
|
} );
|
|
|
|
},
|
|
|
|
},
|
|
|
|
] );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attaches general product screen tracks.
|
|
|
|
*/
|
|
|
|
const attachProductScreenTracks = () => {
|
|
|
|
const initialPublishingData = getPublishingWidgetData();
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelector( '#post-preview' )
|
|
|
|
?.addEventListener( 'click', () => {
|
|
|
|
recordEvent( 'product_preview_changes' );
|
|
|
|
} );
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelector( '.submitduplicate' )
|
|
|
|
?.addEventListener( 'click', () => {
|
|
|
|
recordEvent( 'product_copy', getProductData() );
|
|
|
|
} );
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelector( '.submitdelete' )
|
|
|
|
?.addEventListener( 'click', () => {
|
|
|
|
recordEvent( 'product_delete', getProductData() );
|
|
|
|
} );
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelectorAll(
|
|
|
|
'.edit-post-status, .edit-visibility, .edit-timestamp, .edit-catalog-visibility'
|
|
|
|
)
|
|
|
|
.forEach( ( button ) => {
|
|
|
|
button.addEventListener( 'click', () => {
|
|
|
|
recordEvent( 'product_publish_widget_edit', {
|
|
|
|
...getPublishingWidgetData(),
|
|
|
|
...getProductData(),
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelectorAll(
|
|
|
|
'.save-post-status, .save-post-visibility, .save-timestamp, .save-post-visibility'
|
|
|
|
)
|
|
|
|
.forEach( ( button ) => {
|
|
|
|
button.addEventListener( 'click', () => {
|
|
|
|
recordEvent( 'product_publish_widget_save', {
|
|
|
|
...prefixObjectKeys( getPublishingWidgetData(), 'new_' ),
|
|
|
|
...prefixObjectKeys( initialPublishingData, 'current_' ),
|
|
|
|
...getProductData(),
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelectorAll( '.handle-order-lower, .handle-order-higher' )
|
|
|
|
.forEach( ( button ) => {
|
|
|
|
button.addEventListener( 'click', ( event ) => {
|
|
|
|
const postBox = ( event.target as HTMLElement ).closest(
|
|
|
|
'.postbox'
|
|
|
|
);
|
|
|
|
|
|
|
|
if ( ! postBox ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
recordEvent( 'product_widget_order_change', {
|
|
|
|
widget: postBox.id,
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelector( '#show-settings-link' )
|
|
|
|
?.addEventListener( 'click', () => {
|
|
|
|
recordEvent( 'product_screen_options_open' );
|
|
|
|
} );
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelectorAll( '#adv-settings .metabox-prefs input[type=checkbox]' )
|
|
|
|
.forEach( ( input ) => {
|
|
|
|
input.addEventListener( 'change', () => {
|
|
|
|
recordEvent( 'product_screen_elements', {
|
|
|
|
selected_element: ( input as HTMLInputElement ).value,
|
|
|
|
checkbox: ( input as HTMLInputElement ).checked,
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelectorAll( 'input[name="screen_columns"]' )
|
|
|
|
.forEach( ( input ) => {
|
|
|
|
input.addEventListener( 'change', () => {
|
|
|
|
recordEvent( 'product_layout', {
|
|
|
|
selected_layout: ( input as HTMLInputElement ).value,
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
|
|
|
document
|
|
|
|
.querySelector( '#editor-expand-toggle' )
|
|
|
|
?.addEventListener( 'change', ( event ) => {
|
|
|
|
recordEvent( 'product_additional_settings', {
|
|
|
|
checkbox: ( event.target as HTMLInputElement ).checked,
|
|
|
|
} );
|
|
|
|
} );
|
2022-11-29 01:01:10 +00:00
|
|
|
|
|
|
|
document
|
|
|
|
.querySelector(
|
|
|
|
'#woocommerce-product-updated-message-view-product__link'
|
|
|
|
)
|
|
|
|
?.addEventListener( 'click', () => {
|
|
|
|
recordEvent( 'product_view_product_click', getProductData() );
|
|
|
|
} );
|
|
|
|
|
|
|
|
const dismissProductUpdatedButtonSelector =
|
|
|
|
'.notice-success.is-dismissible > button';
|
|
|
|
|
|
|
|
waitUntilElementIsPresent( dismissProductUpdatedButtonSelector, () => {
|
|
|
|
document
|
|
|
|
.querySelector( dismissProductUpdatedButtonSelector )
|
|
|
|
?.addEventListener( 'click', () => {
|
|
|
|
recordEvent( 'product_view_product_dismiss', getProductData() );
|
|
|
|
} );
|
|
|
|
} );
|
2023-04-12 19:37:23 +00:00
|
|
|
};
|
2023-03-14 22:16:01 +00:00
|
|
|
|
2023-04-12 19:37:23 +00:00
|
|
|
/**
|
|
|
|
* Initialize all product screen tracks.
|
|
|
|
*/
|
|
|
|
export const initProductScreenTracks = () => {
|
|
|
|
attachAttributesTracks();
|
|
|
|
attachProductScreenTracks();
|
|
|
|
attachProductTagsTracks();
|
|
|
|
attachProductAttributesTracks();
|
|
|
|
attachProductVariationsTracks();
|
|
|
|
attachProductTabsTracks();
|
|
|
|
attachProductInventoryTabTracks();
|
2022-05-26 20:56:22 +00:00
|
|
|
};
|
2022-12-12 13:56:28 +00:00
|
|
|
|
|
|
|
export function addExitPageListener( pageId: string ) {
|
|
|
|
let productChanged = false;
|
|
|
|
let triggeredDelete = false;
|
|
|
|
|
|
|
|
const deleteButton = document.querySelector( '#submitpost a.submitdelete' );
|
|
|
|
|
|
|
|
if ( deleteButton ) {
|
|
|
|
deleteButton.addEventListener( 'click', function () {
|
|
|
|
triggeredDelete = true;
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
function checkIfSubmitButtonsDisabled() {
|
|
|
|
const submitButtonSelectors = [
|
|
|
|
'#submitpost [type="submit"]',
|
|
|
|
'#submitpost #post-preview',
|
|
|
|
];
|
|
|
|
let isDisabled = false;
|
|
|
|
for ( const sel of submitButtonSelectors ) {
|
|
|
|
document.querySelectorAll( sel ).forEach( ( element ) => {
|
|
|
|
if ( element.classList.contains( 'disabled' ) ) {
|
|
|
|
isDisabled = true;
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
return isDisabled;
|
|
|
|
}
|
2023-03-01 22:36:38 +00:00
|
|
|
window.addEventListener( 'beforeunload', function (/* event */) {
|
2022-12-12 13:56:28 +00:00
|
|
|
// Check if button disabled or triggered delete to see if user saved or deleted the product instead.
|
|
|
|
if ( checkIfSubmitButtonsDisabled() || triggeredDelete ) {
|
|
|
|
productChanged = false;
|
|
|
|
triggeredDelete = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const editor = window.tinymce && window.tinymce.get( 'content' );
|
|
|
|
|
|
|
|
if ( window.wp.autosave ) {
|
|
|
|
productChanged = window.wp.autosave.server.postChanged();
|
|
|
|
} else if ( editor ) {
|
|
|
|
productChanged = ! editor.isHidden() && editor.isDirty();
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
|
|
|
addCustomerEffortScoreExitPageListener( pageId, () => {
|
|
|
|
return productChanged;
|
|
|
|
} );
|
|
|
|
}
|