Commit 88bf7b5b99 for woocommerce

commit 88bf7b5b99eab5cb69899af6f33349e9f7db88e4
Author: Chi-Hsuan Huang <chihsuan.tw@gmail.com>
Date:   Thu Apr 24 17:02:11 2025 +0800

    [Blueprint] Implement event tracking and fix error reporting (#57336)

    * Enhance Blueprint error handling and tracking in upload and export processes

    * Update error messages to use translation functions for better localization.
    * Implement event tracking for successful and failed blueprint imports and exports.
    * Refactor error reporting to provide more detailed feedback on import/export issues.
    * Add translation support for various error messages related to blueprint file handling.

    * Add changefile(s) from automation for the following project(s): woocommerce, woocommerce/client/admin

    * Refactor error handling in Blueprint settings and update styles

    * Rename error state variable for clarity from `error` to `exportError`.
    * Update error handling to display export errors with a new notice component.
    * Remove unused error notice from the upload dropzone.
    * Enhance styling for error notices in settings to improve visibility and consistency.

    ---------

    Co-authored-by: github-actions <github-actions@github.com>
    Co-authored-by: Vladimir Reznichenko <kalessil@gmail.com>

diff --git a/plugins/woocommerce/changelog/57336-wooplug-3935-blueprint-add-tracks-events b/plugins/woocommerce/changelog/57336-wooplug-3935-blueprint-add-tracks-events
new file mode 100644
index 0000000000..5aef56631c
--- /dev/null
+++ b/plugins/woocommerce/changelog/57336-wooplug-3935-blueprint-add-tracks-events
@@ -0,0 +1,4 @@
+Significance: patch
+Type: update
+
+Implement event tracking and fix error reporting for blueprint
\ No newline at end of file
diff --git a/plugins/woocommerce/client/admin/client/blueprint/components/BlueprintUploadDropzone.tsx b/plugins/woocommerce/client/admin/client/blueprint/components/BlueprintUploadDropzone.tsx
index eec1077398..9d3f8a4e90 100644
--- a/plugins/woocommerce/client/admin/client/blueprint/components/BlueprintUploadDropzone.tsx
+++ b/plugins/woocommerce/client/admin/client/blueprint/components/BlueprintUploadDropzone.tsx
@@ -10,7 +10,7 @@ import {
 	Icon,
 } from '@wordpress/components';
 import { closeSmall, upload } from '@wordpress/icons';
-import { __ } from '@wordpress/i18n';
+import { __, sprintf } from '@wordpress/i18n';
 import { useMachine } from '@xstate5/react';
 import {
 	assertEvent,
@@ -21,6 +21,7 @@ import {
 } from 'xstate5';
 import apiFetch from '@wordpress/api-fetch';
 import { dispatch } from '@wordpress/data';
+import { recordEvent } from '@woocommerce/tracks';

 /**
  * Internal dependencies
@@ -51,7 +52,9 @@ const parseBlueprintSteps = async ( file: File ) => {

 	// Ensure the parsed data is an array
 	if ( ! Array.isArray( steps ) ) {
-		throw new Error( 'Invalid JSON format: Expected an array.' );
+		throw new Error(
+			__( 'Invalid JSON format: Expected an array.', 'woocommerce' )
+		);
 	}

 	return steps;
@@ -70,7 +73,9 @@ const importBlueprint = async ( steps: BlueprintStep[] ) => {
 	try {
 		// Ensure the parsed data is an array
 		if ( ! Array.isArray( steps ) ) {
-			throw new Error( 'Invalid JSON format: Expected an array.' );
+			throw new Error(
+				__( 'Invalid JSON format: Expected an array.', 'woocommerce' )
+			);
 		}

 		const MAX_STEP_SIZE_BYTES =
@@ -90,13 +95,17 @@ const importBlueprint = async ( steps: BlueprintStep[] ) => {
 						{
 							step: step.step,
 							type: 'error',
-							message: `Step exceeds maximum size limit of ${ (
-								MAX_STEP_SIZE_BYTES /
-								( 1024 * 1024 )
-							).toFixed( 2 ) }MB (Current: ${ (
-								stepSize /
-								( 1024 * 1024 )
-							).toFixed( 2 ) }MB)`,
+							message: sprintf(
+								/* translators: 1: Maximum size in MB, 2: Current size in MB */ __(
+									'Step exceeds maximum size limit of %1$.2fMB (Current: %2$.2fMB)',
+									'woocommerce'
+								),
+								(
+									MAX_STEP_SIZE_BYTES /
+									( 1024 * 1024 )
+								).toFixed( 2 ),
+								( stepSize / ( 1024 * 1024 ) ).toFixed( 2 )
+							),
 						},
 					],
 				} );
@@ -134,7 +143,7 @@ const importBlueprint = async ( steps: BlueprintStep[] ) => {
 		dispatch( 'core/notices' ).createSuccessNotice( errorMessage );
 		return errors;
 	} catch ( e ) {
-		throw new Error( 'Error reading or parsing file' );
+		throw e;
 	}
 };

@@ -184,20 +193,27 @@ export const fileUploadMachine = setup( {
 				settings_to_overwrite: event.output.settings_to_overwrite,
 			} );
 		} ),
-		reportError: ( { event } ) => {
-			if ( event.type === 'ERROR' ) {
-				return assign( {
-					error: event.error,
-				} );
-			} else if (
-				event.type === 'xstate.error.actor.0.fileUpload.uploading' ||
-				event.type === 'xstate.error.actor.0.fileUpload.importer'
-			) {
-				return assign( {
-					error: event.output,
-				} );
+		reportError: assign( ( { event } ) => {
+			recordEvent( 'blueprint_import_error' );
+
+			const error = new Error(
+				// default error message if no error is provided
+				__(
+					'An error occurred while importing your Blueprint.',
+					'woocommerce'
+				)
+			);
+
+			if ( 'error' in event ) {
+				error.message = event.error.message;
+			} else if ( 'output' in event && 'message' in event.output ) {
+				error.message = event.output.message;
 			}
-		},
+
+			return {
+				error,
+			};
+		} ),
 	},
 	actors: {
 		importer: fromPromise(
@@ -267,7 +283,11 @@ export const fileUploadMachine = setup( {
 					target: 'error',
 					actions: assign( {
 						error: new Error(
-							'Error reading or parsing file. Please check the schema.'
+							/* translators: Error message when the file is not a valid Blueprint. */
+							__(
+								'Error reading or parsing file. Please check the schema.',
+								'woocommerce'
+							)
 						),
 					} ),
 				},
@@ -312,7 +332,6 @@ export const fileUploadMachine = setup( {
 									name: 'BlueprintImportError',
 									message: event.output
 										.map( ( item ) => {
-											const step = `step: ${ item.step }`;
 											const errors = item.messages
 												.filter(
 													( msg ) =>
@@ -323,7 +342,16 @@ export const fileUploadMachine = setup( {
 														`  ${ msg.message.trim() }.`
 												) // Trim and append a period
 												.join( '\n' ); // Join messages with newlines
-											return `${ step }\nerrors:\n${ errors }`;
+
+											return sprintf(
+												/* translators: 1: Step name 2: Error messages */
+												__(
+													'Step: %1$s Errors: %2$s',
+													'woocommerce'
+												),
+												item.step,
+												errors
+											);
 										} )
 										.join( '\n\n' ),
 								};
@@ -337,6 +365,14 @@ export const fileUploadMachine = setup( {
 			},
 		},
 		importSuccess: {
+			entry: ( { context } ) => {
+				recordEvent( 'blueprint_import_success', {
+					has_partial_errors: Boolean(
+						context.error?.name === 'BlueprintImportError'
+					),
+					steps_count: context.steps?.length || 0,
+				} );
+			},
 			always: 'idle',
 		},
 	},
diff --git a/plugins/woocommerce/client/admin/client/blueprint/components/style.scss b/plugins/woocommerce/client/admin/client/blueprint/components/style.scss
index fc1fd43c66..22c34a6429 100644
--- a/plugins/woocommerce/client/admin/client/blueprint/components/style.scss
+++ b/plugins/woocommerce/client/admin/client/blueprint/components/style.scss
@@ -1,29 +1,6 @@
-$font-sf-pro-display: "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;

 .blueprint-upload-dropzone-error {
 	margin-top: 16px;
-	.components-notice.is-error {
-		background: #fce2e4;
-		border-left-color: #cc1818;
-		padding: 0;
-		color: #1e1e1e;
-		button.components-notice__dismiss {
-			align-self: inherit;
-			padding: 12px;
-			svg {
-				fill: #1e1e1e;
-				width: 15px;
-			}
-		}
-		.components-notice__content {
-			margin: 0;
-			pre {
-				margin: 0;
-				padding: 12px;
-				font-family: $font-sf-pro-display;
-			}
-		}
-	}
 }

 .blueprint-upload-form {
diff --git a/plugins/woocommerce/client/admin/client/blueprint/settings/slotfill.js b/plugins/woocommerce/client/admin/client/blueprint/settings/slotfill.js
index 7454e58131..b675a810b7 100644
--- a/plugins/woocommerce/client/admin/client/blueprint/settings/slotfill.js
+++ b/plugins/woocommerce/client/admin/client/blueprint/settings/slotfill.js
@@ -14,6 +14,7 @@ import { registerPlugin, getPlugin } from '@wordpress/plugins';
 import { __, sprintf } from '@wordpress/i18n';
 import { CollapsibleContent } from '@woocommerce/components';
 import { settings, plugins, layout } from '@wordpress/icons';
+import { recordEvent } from '@woocommerce/tracks';

 /**
  * Internal dependencies
@@ -33,7 +34,7 @@ const icons = {

 const Blueprint = () => {
 	const [ exportEnabled, setExportEnabled ] = useState( true );
-	const [ error, setError ] = useState( null );
+	const [ exportError, setExportError ] = useState( null );

 	const blueprintStepGroups =
 		window.wcSettings?.admin?.blueprint_step_groups || [];
@@ -85,8 +86,19 @@ const Blueprint = () => {
 			if ( url ) {
 				window.URL.revokeObjectURL( url );
 			}
+
+			recordEvent( 'blueprint_export_success', {
+				has_plugins: _steps.plugins?.length > 0,
+				has_themes: _steps.themes?.length > 0,
+				has_settings: _steps.settings?.length > 0,
+				settings_exported: _steps.settings,
+			} );
 		} catch ( e ) {
-			setError( e.message );
+			setExportError( e.message );
+
+			recordEvent( 'blueprint_export_error', {
+				error_message: e.message || 'unknown',
+			} );
 		}

 		setExportEnabled( true );
@@ -105,17 +117,6 @@ const Blueprint = () => {

 	return (
 		<div className="blueprint-settings-slotfill">
-			{ error && (
-				<Notice
-					status="error"
-					onRemove={ () => {
-						setError( null );
-					} }
-					isDismissible
-				>
-					{ error }
-				</Notice>
-			) }
 			<h3>{ __( 'Blueprint', 'woocommerce' ) }</h3>
 			<p className="blueprint-settings-intro-text">
 				{ createInterpolateElement(
@@ -130,6 +131,9 @@ const Blueprint = () => {
 								target="_blank"
 								className="woocommerce-admin-inline-documentation-link"
 								rel="noreferrer"
+								onClick={ () => {
+									recordEvent( 'blueprint_learn_more_click' );
+								} }
 							>
 								{ __( 'Learn more', 'woocommerce' ) }
 							</a>
@@ -152,6 +156,18 @@ const Blueprint = () => {
 					'woocommerce'
 				) }
 			</p>
+			{ exportError && (
+				<Notice
+					className="blueprint-export-error"
+					status="error"
+					onRemove={ () => {
+						setExportError( null );
+					} }
+					isDismissible
+				>
+					{ exportError }
+				</Notice>
+			) }
 			{ blueprintStepGroups.map( ( group, index ) => (
 				<div key={ index } className="blueprint-settings-export-group">
 					<Icon
diff --git a/plugins/woocommerce/client/admin/client/blueprint/settings/style.scss b/plugins/woocommerce/client/admin/client/blueprint/settings/style.scss
index 20c2f627cf..1012d76164 100644
--- a/plugins/woocommerce/client/admin/client/blueprint/settings/style.scss
+++ b/plugins/woocommerce/client/admin/client/blueprint/settings/style.scss
@@ -160,6 +160,31 @@
 	.blueprint-settings-export-button {
 		margin-top: 14px;
 	}
+
+	.components-notice.is-error {
+		background: #fce2e4;
+		border-left-color: #cc1818;
+		padding: 12px;
+
+		button.components-notice__dismiss {
+			align-self: inherit;
+			svg {
+				fill: #1e1e1e;
+				width: 15px;
+			}
+		}
+
+		.components-notice__content {
+			margin: 0;
+
+			pre {
+				color: $gray-900;
+				line-height: 24px; /* 184.615% */
+				margin: 0;
+				font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+			}
+		}
+	}
 }

 // Show the notice at the bottom right of the screen
@@ -233,3 +258,6 @@
 	}
 }

+.blueprint-export-error {
+	margin: 16px 0;
+}