You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add persisted per-user Site Goals settings so saved side panel selections survive dashboard reloads and are restored consistently for both the side panel and Site Goals widgets.
Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
The Site Goals side panel settings are saved in the database per user.
If two admins saved different site goals settings, they will see different data in both widgets.
Reloading the dashboard page loads selected Site Goals settings by the current user from the database and uses them by both widgets and the side panel.
Side panel and widgets fall back to the default selection when no settings are saved for the current user.
Extend User_Setting with option name googlesitekit_analytics-4_site_goals_settings
Constructor accepts User_Options instance
Type: object, default: empty array
merge( array $partial ) method: reads current settings, filters out null values, intersects with allowed keys (goalDrivers, visitorEngagement), merges into existing settings, saves
Sanitize callback: validates top-level keys are arrays, delegates each to sanitize_goal_type_selections() which validates ecommerce and lead sub-keys as string arrays via Sanitize::sanitize_string_list()
Follow the audience-settings.js pattern within the Analytics module datastore
Create fetchGetSiteGoalsSettingsStore via createFetchStore: GET from modules/analytics-4/site-goals-settings, useCache: false
Reducer stores response into state.siteGoalsSettings.settings and state.siteGoalsSettings.savedSettings
Create fetchSaveSiteGoalsSettingsStore via createFetchStore: POST to modules/analytics-4/save-site-goals-settings with { settings } body, isAction: true, same reducer callback
Params validated via validateSiteGoalsSettings
validateSiteGoalsSettings / validateGoalTypeSelections: invariant checks that settings is a plain object, goalDrivers / visitorEngagement (if present) are plain objects with optional ecommerce / lead arrays
Resolves current settings via resolveSelect( MODULES_ANALYTICS_4 ).getSiteGoalsSettings()
Merges current settings with provided partial
Dispatches fetchSaveSiteGoalsSettings with the merged result
On error, sets action error. Returns { response, error }
Resolver getSiteGoalsSettings: fetches if state.siteGoalsSettings?.settings is undefined
Selectors:
getSiteGoalsSettings( state ) > state.siteGoalsSettings?.settings
getSiteGoalsGoalDrivers( state ) > state.siteGoalsSettings?.settings?.goalDrivers
getSiteGoalsVisitorEngagement( state ) > state.siteGoalsSettings?.settings?.visitorEngagement
isSavingSiteGoalsSettings( state ) > checks state.isFetchingSaveSiteGoalsSettings for any truthy value
In assets/js/modules/analytics-4/datastore/index.js, import and add siteGoalsSettings to combineStores
Selection panel - initialization from persisted settings
In assets/js/modules/analytics-4/components/site-goals/selection-panel/index.tsx
Read effectiveDrivers via select( MODULES_ANALYTICS_4 ).getSiteGoalsGoalDrivers() instead of CORE_FORMSSITE_GOALS_EFFECTIVE_DRIVERS
Read effectiveVisitorEngagement via select( MODULES_ANALYTICS_4 ).getSiteGoalsVisitorEngagement() instead of CORE_FORMSSITE_GOALS_EFFECTIVE_VISITOR_ENGAGEMENT
onSideSheetOpen callback: normalize via resolveGoalDriverSelectionState( effectiveDrivers || SITE_GOALS_DEFAULT_SELECTED_DRIVERS ) and resolveVisitorEngagementSelectionState( effectiveVisitorEngagement || SITE_GOALS_DEFAULT_SELECTED_VISITOR_ENGAGEMENT ), then set as staged CORE_FORMS values (SITE_GOALS_SELECTED_DRIVERS, SITE_GOALS_SELECTED_VISITOR_ENGAGEMENT)
Note: CORE_FORMS is retained only for in-panel staged/draft state (SITE_GOALS_SELECTED_DRIVERS, SITE_GOALS_SELECTED_VISITOR_ENGAGEMENT) which is transient and never persisted. CORE_UI is retained only for ephemeral panel-open state (SITE_GOALS_SELECTION_PANEL_OPENED_KEY). We need this currently for snapshoting the the flow, the usage of core forms will be replaced in separate issue that is aligning a new approach for all side panels
Selection panel footer - save flow
In assets/js/modules/analytics-4/components/site-goals/selection-panel/Footer.tsx
If error is returned, return { error } early (panel stays open, error notice shown by SaveErrorNotice)
Only on success: update CORE_FORMS staged values, return { error } (which is undefined)
Remove writes to SITE_GOALS_EFFECTIVE_DRIVERS / SITE_GOALS_EFFECTIVE_VISITOR_ENGAGEMENT in CORE_FORMS — these are superseded by MODULES_ANALYTICS_4 selectors and are now dead code
Wire isBusy={ isSavingSiteGoalsSettings } from select( MODULES_ANALYTICS_4 ) to SelectionPanelFooter
Wire savedItemSlugs from persisted MODULES_ANALYTICS_4 goal drivers (flattened via flattenSelections( resolveGoalDriverSelectionState( savedDrivers ) )), replacing the previous hardcoded []
Widget updates - read from MODULES_ANALYTICS_4
In assets/js/modules/analytics-4/components/site-goals/widgets/OnlineStorePerformanceWidget.tsx
Replace CORE_FORMS.getValue( SITE_GOALS_SELECTION_FORM, SITE_GOALS_EFFECTIVE_DRIVERS ) with select( MODULES_ANALYTICS_4 ).getSiteGoalsGoalDrivers()
Replace CORE_FORMS.getValue( SITE_GOALS_SELECTION_FORM, SITE_GOALS_EFFECTIVE_VISITOR_ENGAGEMENT ) with select( MODULES_ANALYTICS_4 ).getSiteGoalsVisitorEngagement()
Remove CORE_FORMS import and SITE_GOALS_EFFECTIVE_* / SITE_GOALS_SELECTION_FORM imports
Keep fallback to SITE_GOALS_DEFAULT_SELECTED_DRIVERS / SITE_GOALS_DEFAULT_SELECTED_VISITOR_ENGAGEMENT when selectors return undefined
In assets/js/modules/analytics-4/components/site-goals/widgets/LeadGenerationPerformanceWidget.tsx
Same pattern: replace CORE_FORMS effective driver read with select( MODULES_ANALYTICS_4 ).getSiteGoalsGoalDrivers()
Remove CORE_FORMS import and related constants
Keep fallback to defaults
Cleanup
Remove SITE_GOALS_EFFECTIVE_DRIVERS and SITE_GOALS_EFFECTIVE_VISITOR_ENGAGEMENT constant definitions from constants.ts if no non-test file references them after the above changes
Feature Description
Add persisted per-user Site Goals settings so saved side panel selections survive dashboard reloads and are restored consistently for both the side panel and Site Goals widgets.
Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
Implementation Brief
Persisted user setting under Analytics module
Add
includes/Modules/Analytics_4/Site_Goals_Settings.phpUser_Settingwith option namegooglesitekit_analytics-4_site_goals_settingsUser_Optionsinstanceobject, default: empty arraymerge( array $partial )method: reads current settings, filters out null values, intersects with allowed keys (goalDrivers,visitorEngagement), merges into existing settings, savessanitize_goal_type_selections()which validatesecommerceandleadsub-keys as string arrays viaSanitize::sanitize_string_list()Add
includes/Modules/Analytics_4/Datapoints/Get_Site_Goals_Settings.phpGet_Audience_Settingsdatapoint patternsite_goals_settingsvia definition arraycreate_requestreturns closure that calls$site_goals_settings->get()VIEW_DASHBOARDAdd
includes/Modules/Analytics_4/Datapoints/Save_Site_Goals_Settings.phpSave_Audience_Settingsdatapoint patternsite_goals_settingsvia definition arraycreate_requestreads$data_request['settings'], calls$site_goals_settings->merge(), returns updated settingsVIEW_DASHBOARDIn
includes/Modules/Analytics_4.phpSite_Goals_Settingswith$this->user_optionsin the constructor$this->site_goals_settings->register()inregister()GET:site-goals-settings>Get_Site_Goals_Settings,POST:save-site-goals-settings>Save_Site_Goals_Settings, passingsite_goals_settingsinstance via definition arraymodules/analytics-4/data/site-goals-settingsviagooglesitekit_apifetch_preload_pathsfilteron_deactivation()(follow audience settings pattern)Datastore partial
Add
assets/js/modules/analytics-4/datastore/site-goals-settings.jsaudience-settings.jspattern within the Analytics module datastorefetchGetSiteGoalsSettingsStoreviacreateFetchStore: GET frommodules/analytics-4/site-goals-settings,useCache: falsestate.siteGoalsSettings.settingsandstate.siteGoalsSettings.savedSettingsfetchSaveSiteGoalsSettingsStoreviacreateFetchStore: POST tomodules/analytics-4/save-site-goals-settingswith{ settings }body,isAction: true, same reducer callbackvalidateSiteGoalsSettingsvalidateSiteGoalsSettings/validateGoalTypeSelections: invariant checks thatsettingsis a plain object,goalDrivers/visitorEngagement(if present) are plain objects with optionalecommerce/leadarrayssiteGoalsSettings: undefinedsaveSiteGoalsSettings( settings )(createValidatedAction):resolveSelect( MODULES_ANALYTICS_4 ).getSiteGoalsSettings()fetchSaveSiteGoalsSettingswith the merged result{ response, error }getSiteGoalsSettings: fetches ifstate.siteGoalsSettings?.settingsisundefinedgetSiteGoalsSettings( state )>state.siteGoalsSettings?.settingsgetSiteGoalsGoalDrivers( state )>state.siteGoalsSettings?.settings?.goalDriversgetSiteGoalsVisitorEngagement( state )>state.siteGoalsSettings?.settings?.visitorEngagementisSavingSiteGoalsSettings( state )> checksstate.isFetchingSaveSiteGoalsSettingsfor any truthy valueIn
assets/js/modules/analytics-4/datastore/index.js, import and addsiteGoalsSettingstocombineStoresSelection panel - initialization from persisted settings
assets/js/modules/analytics-4/components/site-goals/selection-panel/index.tsxeffectiveDriversviaselect( MODULES_ANALYTICS_4 ).getSiteGoalsGoalDrivers()instead ofCORE_FORMSSITE_GOALS_EFFECTIVE_DRIVERSeffectiveVisitorEngagementviaselect( MODULES_ANALYTICS_4 ).getSiteGoalsVisitorEngagement()instead ofCORE_FORMSSITE_GOALS_EFFECTIVE_VISITOR_ENGAGEMENTonSideSheetOpencallback: normalize viaresolveGoalDriverSelectionState( effectiveDrivers || SITE_GOALS_DEFAULT_SELECTED_DRIVERS )andresolveVisitorEngagementSelectionState( effectiveVisitorEngagement || SITE_GOALS_DEFAULT_SELECTED_VISITOR_ENGAGEMENT ), then set as stagedCORE_FORMSvalues (SITE_GOALS_SELECTED_DRIVERS,SITE_GOALS_SELECTED_VISITOR_ENGAGEMENT)SITE_GOALS_EFFECTIVE_DRIVERS/SITE_GOALS_EFFECTIVE_VISITOR_ENGAGEMENTimportsCORE_FORMSis retained only for in-panel staged/draft state (SITE_GOALS_SELECTED_DRIVERS,SITE_GOALS_SELECTED_VISITOR_ENGAGEMENT) which is transient and never persisted.CORE_UIis retained only for ephemeral panel-open state (SITE_GOALS_SELECTION_PANEL_OPENED_KEY). We need this currently for snapshoting the the flow, the usage of core forms will be replaced in separate issue that is aligning a new approach for all side panelsSelection panel footer - save flow
assets/js/modules/analytics-4/components/site-goals/selection-panel/Footer.tsxsaveSettingsasyncCORE_FORMS, callawait dispatch( MODULES_ANALYTICS_4 ).saveSiteGoalsSettings({ goalDrivers: sanitizedSelectionState, visitorEngagement: sanitizedVisitorEngagementSelectionState })erroris returned, return{ error }early (panel stays open, error notice shown bySaveErrorNotice)CORE_FORMSstaged values, return{ error }(which isundefined)SITE_GOALS_EFFECTIVE_DRIVERS/SITE_GOALS_EFFECTIVE_VISITOR_ENGAGEMENTinCORE_FORMS— these are superseded byMODULES_ANALYTICS_4selectors and are now dead codeisBusy={ isSavingSiteGoalsSettings }fromselect( MODULES_ANALYTICS_4 )toSelectionPanelFootersavedItemSlugsfrom persistedMODULES_ANALYTICS_4goal drivers (flattened viaflattenSelections( resolveGoalDriverSelectionState( savedDrivers ) )), replacing the previous hardcoded[]Widget updates - read from MODULES_ANALYTICS_4
In
assets/js/modules/analytics-4/components/site-goals/widgets/OnlineStorePerformanceWidget.tsxCORE_FORMS.getValue( SITE_GOALS_SELECTION_FORM, SITE_GOALS_EFFECTIVE_DRIVERS )withselect( MODULES_ANALYTICS_4 ).getSiteGoalsGoalDrivers()CORE_FORMS.getValue( SITE_GOALS_SELECTION_FORM, SITE_GOALS_EFFECTIVE_VISITOR_ENGAGEMENT )withselect( MODULES_ANALYTICS_4 ).getSiteGoalsVisitorEngagement()CORE_FORMSimport andSITE_GOALS_EFFECTIVE_*/SITE_GOALS_SELECTION_FORMimportsSITE_GOALS_DEFAULT_SELECTED_DRIVERS/SITE_GOALS_DEFAULT_SELECTED_VISITOR_ENGAGEMENTwhen selectors returnundefinedIn
assets/js/modules/analytics-4/components/site-goals/widgets/LeadGenerationPerformanceWidget.tsxCORE_FORMSeffective driver read withselect( MODULES_ANALYTICS_4 ).getSiteGoalsGoalDrivers()CORE_FORMSimport and related constantsCleanup
SITE_GOALS_EFFECTIVE_DRIVERSandSITE_GOALS_EFFECTIVE_VISITOR_ENGAGEMENTconstant definitions fromconstants.tsif no non-test file references them after the above changesTest Coverage
Add
assets/js/modules/analytics-4/datastore/site-goals-settings.test.jsgetSiteGoalsSettings,getSiteGoalsGoalDrivers,getSiteGoalsVisitorEngagementpopulated after receivesaveSiteGoalsSettingswith partial data posts merged settings to endpoint, updates store on successsaveSiteGoalsSettingsreturns{ error }when the POST failsisSavingSiteGoalsSettingsreturnstruewhile save is in flightvisitorEngagement) preserves existinggoalDriversUpdate
assets/js/modules/analytics-4/components/site-goals/selection-panel/index.test.tsxMODULES_ANALYTICS_4site goals settings inbeforeEachMODULES_ANALYTICS_4.getSiteGoalsGoalDrivers()reflects saved state after saveMODULES_ANALYTICS_4.getSiteGoalsVisitorEngagement()reflects saved stateUpdate widget test files (
OnlineStorePerformanceWidget.test.tsx,LeadGenerationPerformanceWidget.test.tsx)MODULES_ANALYTICS_4site goals settings inbeforeEachUpdate story files (
selection-panel/index.stories.tsx,OnlineStorePerformanceWidget.stories.tsx,LeadGenerationPerformanceWidget.stories.tsx)MODULES_ANALYTICS_4site goals settings in common setupAdd PHP unit tests for
Site_Goals_Settingsget()returns empty array by defaultmerge()savesgoalDriversandvisitorEngagementcorrectlymerge()with partial data preserves existing settingsmerge()ignores unknown keysAdd PHP unit tests for
Get_Site_Goals_SettingsandSave_Site_Goals_SettingsdatapointsQA Brief
Changelog entry