Skip to content

Add WooCommerce Tracks integration and the Onboarding funnel (4649) #3446

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: trunk
Choose a base branch
from

Conversation

danieldudzic
Copy link
Collaborator

Description

This PR will introduce a tracking system for the onboarding flow with source-based field filtering, multi-funnel support, and extensible adapter architecture. The system prevents tracking loops and provides comprehensive analytics capabilities across the new Settings UI.

It monitors WordPress data stores rather than adding code to frontend components, ensuring comprehensive coverage of all state changes regardless of their source (user actions, API responses, system updates) while maintaining clean separation of concerns.

Core Components

1. Registry System (registry.js)
  • Registers tracking funnels with configurations
  • Maps stores to funnels to prevent duplicate subscriptions
  • Enables dynamic funnel activation based on conditions
registerTrackingFunnel('onboarding', {
    stores: ['wc/paypal/onboarding', 'wc/paypal/common'],
    trackingCondition: {
        store: 'wc/paypal/common',
        selector: 'merchant',
        field: 'isConnected',
        expectedValue: false
    }
});
2. Subscription Manager (subscription-manager.js)
  • Prevents subscription conflicts when multiple funnels track the same store
  • Routes store changes to relevant funnels
  • Manages funnel lifecycle based on conditions
// Single subscription handles multiple funnels
ensureStoreSubscription('wc/paypal/onboarding');
// Routes to: onboarding funnel, settings funnel, etc.
3. Source-Based Field Filtering (utils.js, subscription-manager.js)
  • Field-level source rules define which change sources trigger tracking
  • Prevents tracking loops by filtering out subscription/internal updates
  • Enables selective tracking (user-only vs user+system)
fieldRules: {
    step: { allowedSources: ['user', 'system'] },
    isCasualSeller: { allowedSources: ['user'] }
}
// Only tracks user-initiated account type changes

Source tracking needs to be added because Redux store subscriptions and internal system updates create noise in analytics by triggering tracking events for non-user actions.

4. Adapter Pattern (adapters/woocommerce-tracks.js, adapters/console-logger.js)
  • WooCommerce Tracks Adapter for analytics
  • Console Logger Adapter for debugging
  • Extensible for additional platforms
trackingService.addAdapter(new WooCommerceTracksAdapter());
trackingService.addAdapter(new ConsoleLoggerAdapter());
// Events sent to both destinations
5. Core Tracking Service (services/funnel-tracking.js)
  • Orchestrates the entire tracking process for each funnel
  • Manages adapters and processes state changes
  • Handles source filtering and field rule validation
  • Coordinates funnel-specific translation functions
  • Provides session management and debugging utilities
6. Funnel Configuration (funnels/onboarding.js)
  • Defines tracking configuration for the onboarding flow
  • Specifies which stores and fields to track
  • Sets up tracking conditions and field rules

Implementation

  • Action Enhancement: Add source parameter to setPersistent/setTransient actions
  • Store Integration: Add field source tracking to data stores
  • Component Updates: Form components pass appropriate source values ('user', 'system')
  • Onboarding Funnel: Track step progression, feature selection, and connection events

Testing

  1. Enable WooCommerce Tracks Debug:
    Add to the browser console:
    localStorage.setItem('debug', 'wc-admin:*');
  2. Keep the browser console open and trigger events by:
    • Starting onboarding flow
    • Changing account type/product selection
    • Completing onboarding steps
  3. Verify Events: Look for ppcp_onboarding_ prefixed events in console

Comment on lines +77 to +95
const updateFieldSources = ( currentSources, fieldName, source ) => {
if ( ! source ) {
return currentSources;
}

return {
...currentSources,
[ fieldName ]: {
source,
timestamp: Date.now(),
},
};
};

const clearFieldSource = ( currentSources, fieldName ) => {
const newSources = { ...currentSources };
delete newSources[ fieldName ];
return newSources;
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm uncertain if I like this direct integration into the reducer, as it implements tracking on this store only. This (a) does not scale well and (b) changes the responsibility of the store to handle two things at once

action.source
);
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While working, this pattern is not ideal for several reasons:

  1. it adds bloat to the code and makes reducers look more complex than they are.
  2. I think tracking should not happen on reducer level, but on action level.
  3. It violates the single responsibility

My solution would be to create a new store for tracking with two actions: updateSources and clearSources. Then extend createHooksForStore() to dispatch the updateSources action right after calling the original dispatcher, like this:

const setValue = useCallback(
    (newValue, source = null) => { // ← add optional tracking argument here
        actions[dispatcher](key, newValue);
        
        if (source) { // ← dispatch conditional tracking here
            const trackingActions = useDispatch('tracking-store');
            trackingActions.updateSources(key, source);
        }
    },
    [actions, key]
);

getFunnelsForStore,
getTrackingStatus,
} from './registry';

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With a dedicated Redux store for tracking, this registry would become much simpler and reliable, as most of its logic would be part of the Redux store (a pattern that's more aligned with the code base)

Copy link
Collaborator

@strangerkir strangerkir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Only one question to make sure there is no unintentional regression.

@@ -38,7 +38,7 @@ const StepProducts = () => {
};

initChoices();
}, [ canUseSubscriptions, optionState, products, setProducts ] );
}, [ canUseSubscriptions, isCasualSeller ] );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume these dependencies were removed intentionally, and this is okay?

return false;
}

// Check store readiness.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've noticed a growing tendency to add more inline comments in the codebase. Personally, I believe inline comments are valuable when they clarify something non-obvious or explain edge cases, but in general, I prefer using them only when truly necessary.

Since some developers use them frequently and others rarely, it might be helpful to align on a shared approach to inline comments as a team. Perhaps we can discuss this outside of this PR to establish a consistent convention?

Copy link
Collaborator

@Narek13 Narek13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really great PR, thanks for the work on it @danieldudzic 🙌 I’ve also briefly tested it locally, and the code works fine for me.

One thing we might want to consider is what Philipp suggested: refactoring the tracking logic into a dedicated Redux store. But current PR looks good to me.

Copy link
Collaborator

@AlexP11223 AlexP11223 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM except the above comments.

@hmouhtar
Copy link
Collaborator

LGTM 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants