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;
+}