Repository: wordpress-mobile/WordPress-Android (language: kotlin)

Issue:
Dashboard Personalization: Create Personalize Your Home Tab Card
Create the **Personalize Home Tab** card 

![Image](https://github.com/wordpress-mobile/WordPress-Android/assets/506707/5624c751-af13-40bb-9100-f02a14d6c0de)


```[tasklist]
### Tasks
- [x] Guard with `dashboard_personalization` feature flag
- [x] Create compose UI, viewmodel, builder, etc
- [x] Slot as last card on the dashboard
- [x] On card tap: navigate to Personalize Home Tab View (Add new navigation event)
- [x] On card tap: track `my_site_dashboard_card_item_tapped` with property `type:personalize`
- [x] On card shown: track `my_site_dashboard_card_shown` with property `type:personalize`
```



Discussion hints:

Pull request: Dashboard Personalization: Personalize card navigation, tracking and unit testing
Closes #18937 

This PR adds the following for the personalize card
- Navigation event for onclick
- Tracking for card shown and tapped
- Add unit tests

**To test:**
- Install the app and login
- Navigate to Me > Debug Settings > enable `dashboard_personalization` and restart the app
- Navigate to the My Site tab
- ✅ Verify log contains: 🔵 Tracked: my_site_dashboard_card_shown, Properties: {"type":"personalize","subtype":"personalize"}
- Tap on the personalize card
- ✅ Verify log contains: 🔵 Tracked: my_site_dashboard_card_item_tapped, Properties: {"type":"personalize","subtype":"personalize"}

## Regression Notes
1. Potential unintended areas of impact
Personalize card tracking and events do not work correctly

2. What I did to test those areas of impact (or what existing automated tests I relied on)
Manual testing and new unit tests `PersonalizeCardViewModelSliceTest` and `PersonalizeCardBuilderTest`

3. What automated tests I added (or what prevented me from doing so) N/A

PR submission checklist:

- [x] I have completed the Regression Notes.
- [x] I have considered adding accessibility improvements for my changes.
- [x] I have considered if this change warrants user-facing release notes and have added them to `RELEASE-NOTES.txt` if necessary.

UI Changes testing checklist: N/A


Test command: ./gradlew :WordPress:testWordpressVanillaDebugUnitTest --tests "org.wordpress.android.ui.mysite.cards.personalize.PersonalizeCardViewModelSliceTest" --tests "org.wordpress.android.ui.mysite.cards.personalize.PersonalizeCardBuilderTest"

Fix patch:
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/ActivityNavigator.kt b/WordPress/src/main/java/org/wordpress/android/ui/ActivityNavigator.kt
index 16406fd9a874..d6aa4ad91098 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/ActivityNavigator.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/ActivityNavigator.kt
@@ -11,6 +11,7 @@ import org.wordpress.android.ui.blaze.blazecampaigns.campaignlisting.CampaignLis
 import org.wordpress.android.ui.blaze.blazepromote.ARG_BLAZE_FLOW_SOURCE
 import org.wordpress.android.ui.blaze.blazepromote.ARG_BLAZE_SHOULD_SHOW_OVERLAY
 import org.wordpress.android.ui.blaze.blazepromote.BlazePromoteParentActivity
+import org.wordpress.android.ui.mysite.personalisation.PersonalisationActivity
 import javax.inject.Inject
 import javax.inject.Singleton
 
@@ -59,4 +60,8 @@ class ActivityNavigator @Inject constructor() {
     ) {
         WPWebViewActivity.openUrlByUsingGlobalWPCOMCredentials(context, url)
     }
+
+    fun openDashboardPersonalization(context: Context) {
+        context.startActivity(Intent(context, PersonalisationActivity::class.java))
+    }
 }
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteViewModel.kt
index fa8e2107a96e..1cfcf073ea66 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteViewModel.kt
@@ -282,7 +282,8 @@ class MySiteViewModel @Inject constructor(
         postsCardViewModelSlice.onNavigation,
         activityLogCardViewModelSlice.onNavigation,
         siteItemsViewModelSlice.onNavigation,
-        bloggingPromptCardViewModelSlice.onNavigation
+        bloggingPromptCardViewModelSlice.onNavigation,
+        personalizeCardViewModelSlice.onNavigation
     )
     val onMediaUpload = _onMediaUpload as LiveData<Event<MediaModel>>
     val onUploadedItem = siteIconUploadHandler.onUploadedItem
@@ -1364,6 +1365,8 @@ class MySiteViewModel @Inject constructor(
         siteSelected.cardAndItems.filterIsInstance<JetpackInstallFullPluginCard>()
             .forEach { jetpackInstallFullPluginShownTracker.trackShown(it.type, quickStartRepository.currentTab) }
         dashboardCardPlansUtils.trackCardShown(viewModelScope, siteSelected)
+        siteSelected.dashboardCardsAndItems.filterIsInstance<MySiteCardAndItem.Card.PersonalizeCardModel>()
+            .forEach { personalizeCardViewModelSlice.trackShown(it.type) }
     }
 
     private fun resetShownTrackers() {
@@ -1372,6 +1375,7 @@ class MySiteViewModel @Inject constructor(
         quickStartTracker.resetShown()
         jetpackFeatureCardShownTracker.resetShown()
         jetpackInstallFullPluginShownTracker.resetShown()
+        personalizeCardViewModelSlice.resetShown()
     }
 
     private fun trackTabChanged(isSiteMenu: Boolean) {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/SiteNavigationAction.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/SiteNavigationAction.kt
index 6ca47cf0eb8a..10fed8d798d3 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/SiteNavigationAction.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/SiteNavigationAction.kt
@@ -104,6 +104,7 @@ sealed class SiteNavigationAction {
         SiteNavigationAction()
 
     data class OpenDomainTransferPage(val url: String) : SiteNavigationAction()
+    object OpenDashboardPersonalization : SiteNavigationAction()
 }
 
 sealed class BloggingPromptCardNavigationAction: SiteNavigationAction() {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/CardsTracker.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/CardsTracker.kt
index 41fe5f1eda5e..80ce6b0a7f39 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/CardsTracker.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/dashboard/CardsTracker.kt
@@ -30,6 +30,7 @@ class CardsTracker @Inject constructor(
         ACTIVITY("activity_log"),
         DASHBOARD_CARD_PLANS("dashboard_card_plans"),
         DASHBOARD_CARD_DOMAIN_TRANSFER("dashboard_card_domain_transfer"),
+        PERSONALIZE_CARD("personalize")
     }
 
     enum class QuickStartSubtype(val label: String) {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardShownTracker.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardShownTracker.kt
new file mode 100644
index 000000000000..cf94d65d7e28
--- /dev/null
+++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardShownTracker.kt
@@ -0,0 +1,32 @@
+package org.wordpress.android.ui.mysite.cards.personalize
+
+import org.wordpress.android.analytics.AnalyticsTracker
+import org.wordpress.android.ui.mysite.MySiteCardAndItem
+import org.wordpress.android.ui.mysite.cards.dashboard.CardsTracker
+import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper
+import javax.inject.Inject
+
+class PersonalizeCardShownTracker @Inject constructor(
+    private val analyticsTrackerWrapper: AnalyticsTrackerWrapper
+) {
+    private val cardsShownTracked = mutableListOf<MySiteCardAndItem.Type>()
+
+    fun resetShown() {
+        cardsShownTracked.clear()
+    }
+
+    fun trackShown(itemType: MySiteCardAndItem.Type) {
+        if (itemType == MySiteCardAndItem.Type.PERSONALIZE_CARD) {
+            if (!cardsShownTracked.contains(itemType)) {
+                cardsShownTracked.add(itemType)
+                analyticsTrackerWrapper.track(
+                    AnalyticsTracker.Stat.MY_SITE_DASHBOARD_CARD_SHOWN,
+                    mapOf(
+                        CardsTracker.TYPE to CardsTracker.Type.PERSONALIZE_CARD.label,
+                        CardsTracker.SUBTYPE to CardsTracker.Type.PERSONALIZE_CARD.label
+                    )
+                )
+            }
+        }
+    }
+}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardViewModelSlice.kt
index beabb56b3fe8..8936960986b2 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardViewModelSlice.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardViewModelSlice.kt
@@ -1,15 +1,37 @@
 package org.wordpress.android.ui.mysite.cards.personalize
 
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import org.wordpress.android.ui.mysite.MySiteCardAndItem
+import org.wordpress.android.ui.mysite.SiteNavigationAction.OpenDashboardPersonalization
 import org.wordpress.android.ui.mysite.MySiteCardAndItemBuilderParams.PersonalizeCardBuilderParams
+import org.wordpress.android.ui.mysite.SiteNavigationAction
+import org.wordpress.android.ui.mysite.cards.dashboard.CardsTracker
+import org.wordpress.android.viewmodel.Event
 import javax.inject.Inject
-import javax.inject.Singleton
 
-@Singleton
-class PersonalizeCardViewModelSlice@Inject constructor(
+class PersonalizeCardViewModelSlice @Inject constructor(
+    private val cardsTracker: CardsTracker,
+    private val personalizeCardShownTracker: PersonalizeCardShownTracker
 )  {
+    private val _onNavigation = MutableLiveData<Event<SiteNavigationAction>>()
+    val onNavigation = _onNavigation as LiveData<Event<SiteNavigationAction>>
+
     fun getBuilderParams() = PersonalizeCardBuilderParams(onClick = this::onCardClick)
 
     fun onCardClick() {
-        // todo: implement
+        cardsTracker.trackCardItemClicked(
+            CardsTracker.Type.PERSONALIZE_CARD.label,
+            CardsTracker.Type.PERSONALIZE_CARD.label
+        )
+        _onNavigation.value = Event(OpenDashboardPersonalization)
+    }
+
+    fun trackShown(itemType: MySiteCardAndItem.Type) {
+        personalizeCardShownTracker.trackShown(itemType)
+    }
+
+    fun resetShown() {
+        personalizeCardShownTracker.resetShown()
     }
 }
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/personalisation/PersonalisationActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/personalisation/PersonalisationActivity.kt
index 851a93f591c3..ab8b14e5d88c 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/personalisation/PersonalisationActivity.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/personalisation/PersonalisationActivity.kt
@@ -28,11 +28,13 @@ import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.lifecycle.LiveData
+import dagger.hilt.android.AndroidEntryPoint
 import org.wordpress.android.R
 import org.wordpress.android.ui.compose.components.MainTopAppBar
 import org.wordpress.android.ui.compose.components.NavigationIcons
 import org.wordpress.android.ui.compose.theme.AppTheme
 
+@AndroidEntryPoint
 class PersonalisationActivity : ComponentActivity() {
     private val viewModel: PersonalisationViewModel by viewModels()
 
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/tabs/MySiteTabFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/tabs/MySiteTabFragment.kt
index 9068667cf5c1..c3d0611f2ca4 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/tabs/MySiteTabFragment.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/tabs/MySiteTabFragment.kt
@@ -437,6 +437,10 @@ class MySiteTabFragment : Fragment(R.layout.my_site_tab_fragment),
         )
 
         is BloggingPromptCardNavigationAction -> handleNavigation(action)
+
+        is SiteNavigationAction.OpenDashboardPersonalization -> activityNavigator.openDashboardPersonalization(
+            requireActivity()
+        )
     }
 
     private fun handleNavigation(action: BloggingPromptCardNavigationAction) {


Test patch:
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardBuilderTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardBuilderTest.kt
new file mode 100644
index 000000000000..1c24cd8b2cbe
--- /dev/null
+++ b/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardBuilderTest.kt
@@ -0,0 +1,46 @@
+package org.wordpress.android.ui.mysite.cards.personalize
+
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnitRunner
+import org.assertj.core.api.Assertions.assertThat
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+import org.wordpress.android.ui.mysite.MySiteCardAndItemBuilderParams
+import org.wordpress.android.util.config.DashboardPersonalizationFeatureConfig
+
+@RunWith(MockitoJUnitRunner::class)
+class PersonalizeCardBuilderTest {
+    @Mock
+    lateinit var featureConfig: DashboardPersonalizationFeatureConfig
+
+    private lateinit var builder: PersonalizeCardBuilder
+
+    @Before
+    fun setUp() {
+        builder = PersonalizeCardBuilder(featureConfig)
+    }
+
+    @Test
+    fun `given flag is enabled, when build requested, then model is built`() {
+        whenever(featureConfig.isEnabled()).thenReturn(true)
+
+        val result = builder.build(getParams())
+
+        assertThat(result).isNotNull
+    }
+
+    @Test
+    fun `given flag is disabled, when build requested, then model is not built`() {
+        whenever(featureConfig.isEnabled()).thenReturn(false)
+
+        val result = builder.build(getParams())
+
+        assertThat(result).isNull()
+    }
+
+    private fun getParams() =
+         MySiteCardAndItemBuilderParams.PersonalizeCardBuilderParams(mock())
+}
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardViewModelSliceTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardViewModelSliceTest.kt
new file mode 100644
index 000000000000..19798dea39a8
--- /dev/null
+++ b/WordPress/src/test/java/org/wordpress/android/ui/mysite/cards/personalize/PersonalizeCardViewModelSliceTest.kt
@@ -0,0 +1,79 @@
+package org.wordpress.android.ui.mysite.cards.personalize
+
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.assertj.core.api.Assertions.assertThat
+import org.mockito.junit.MockitoJUnitRunner
+import org.mockito.kotlin.verify
+import org.wordpress.android.BaseUnitTest
+import org.wordpress.android.ui.mysite.MySiteCardAndItem
+import org.wordpress.android.ui.mysite.SiteNavigationAction
+import org.wordpress.android.ui.mysite.cards.dashboard.CardsTracker
+
+@ExperimentalCoroutinesApi
+@RunWith(MockitoJUnitRunner::class)
+class PersonalizeCardViewModelSliceTest : BaseUnitTest() {
+    @Mock
+    lateinit var cardsTracker: CardsTracker
+
+    @Mock
+    lateinit var personalizeCardShownTracker: PersonalizeCardShownTracker
+
+    private lateinit var viewModelSlice: PersonalizeCardViewModelSlice
+
+    private lateinit var navigationActions: MutableList<SiteNavigationAction>
+
+    @Before
+    fun setUp() {
+        viewModelSlice = PersonalizeCardViewModelSlice(
+            cardsTracker,
+            personalizeCardShownTracker
+        )
+        navigationActions = mutableListOf()
+        viewModelSlice.onNavigation.observeForever { event ->
+            event?.getContentIfNotHandled()?.let {
+                navigationActions.add(it)
+            }
+        }
+    }
+
+    @Test
+    fun `given personalize card, when card item is clicked, then event is tracked`() =
+        test {
+            val params = viewModelSlice.getBuilderParams()
+
+            params.onClick()
+
+            verify(cardsTracker).trackCardItemClicked(
+                CardsTracker.Type.PERSONALIZE_CARD.label, CardsTracker.Type.PERSONALIZE_CARD.label)
+        }
+
+    @Test
+    fun `given personalize card, when card item is clicked, then open personalize event is raised`() =
+        test {
+            val params = viewModelSlice.getBuilderParams()
+
+            params.onClick()
+
+            assertThat(navigationActions).containsOnly(SiteNavigationAction.OpenDashboardPersonalization)
+        }
+
+    @Test
+    fun `given personalize card, when card shown track requested, then track card shown`() =
+        test {
+            viewModelSlice.trackShown(MySiteCardAndItem.Type.PERSONALIZE_CARD)
+
+            verify(personalizeCardShownTracker).trackShown(MySiteCardAndItem.Type.PERSONALIZE_CARD)
+        }
+
+    @Test
+    fun `given personalize card, when reset shown tracker requested, then track card shown is reset`() =
+        test {
+            viewModelSlice.resetShown()
+
+            verify(personalizeCardShownTracker).resetShown()
+        }
+}