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

Issue:
Stats Traffic: By day card (Today card)
Refer to #19973,  to reuse the card in this when date picker strip is ready 

Parent: https://github.com/wordpress-mobile/jetpack-issue-repo/issues/1
![Image](https://github.com/wordpress-mobile/WordPress-iOS/assets/4062343/a00e6335-235a-4067-9bed-af657c1c9b83)


Discussion hints:

Pull request: Stats Traffic: Today card
Fixes #19950 

-----
<img width=320 src="https://github.com/wordpress-mobile/WordPress-Android/assets/990349/243c2c9b-37a6-4875-a478-6c79037fe957" />



## To Test:
Test 1:
- Enable `stats_traffic_tab` feature flag (Me -> Debug settings - Remote Features)
- Go to Stats
- `Verify` only Traffic tab, and Insights tabs are shown as in after column 
- `Switch` to Traffic tab if not on it already
- `Verify` that Today card is shown in the image above
> [!NOTE] 
> Traffic tab is set to show Days tab for the time being since except for the first card with graph, the rest of the cards remain same.
> Card is not hooked up to the Spinner or Date.  Waiting for that.
> Styling may need update later on

Test 2:

- Disable feature flag
- `Verify` Stats tabs all show up as in before column, and work as before
-----

## Regression Notes

1. Potential unintended areas of impact

    - Tested various combinations

2. What I did to test those areas of impact (or what existing automated tests I relied on)

    - Tested manually

3. What automated tests I added (or what prevented me from doing so)

    - Existing unit tests

-----

## 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:

- [ ] Portrait and landscape orientations.
- [x] Light and dark modes.
- [ ] Fonts: Larger, smaller and bold text.
- [ ] High contrast.
- [ ] Talkback.
- [ ] Languages with large words or with letters/accents not frequently used in English.
- [ ] Right-to-left languages. (Even if translation isn’t complete, formatting should still respect the right-to-left layout)
- [ ] Large and small screen sizes. (Tablet and smaller phones)
- [ ] Multi-tasking: Split screen and Pop-up view. (Android 10 or higher)


Test command: ./gradlew :WordPress:testWordpressVanillaDebugUnitTest --tests "org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.OverviewUseCaseTest"

Fix patch:
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt
index 46d2a1ce3a39..ef7b192d6ba7 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt
@@ -14,6 +14,8 @@ import org.wordpress.android.modules.BG_THREAD
 import org.wordpress.android.modules.UI_THREAD
 import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase
 import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem
+import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.QuickScanItem
+import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.QuickScanItem.Column
 import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ValueItem
 import org.wordpress.android.ui.stats.refresh.lists.sections.granular.GranularUseCaseFactory
 import org.wordpress.android.ui.stats.refresh.lists.sections.granular.SelectedDateProvider
@@ -21,12 +23,14 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.O
 import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetUpdater.StatsWidgetUpdaters
 import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter
 import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider
+import org.wordpress.android.ui.stats.refresh.utils.StatsUtils
 import org.wordpress.android.ui.stats.refresh.utils.toStatsSection
 import org.wordpress.android.ui.stats.refresh.utils.trackGranular
 import org.wordpress.android.util.AppLog
 import org.wordpress.android.util.AppLog.T
 import org.wordpress.android.util.LocaleManagerWrapper
 import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper
+import org.wordpress.android.util.config.StatsTrafficTabFeatureConfig
 import org.wordpress.android.viewmodel.ResourceProvider
 import java.util.Calendar
 import javax.inject.Inject
@@ -48,7 +52,9 @@ class OverviewUseCase constructor(
     private val analyticsTracker: AnalyticsTrackerWrapper,
     private val statsWidgetUpdaters: StatsWidgetUpdaters,
     private val localeManagerWrapper: LocaleManagerWrapper,
-    private val resourceProvider: ResourceProvider
+    private val resourceProvider: ResourceProvider,
+    private val statsUtils: StatsUtils,
+    private val trafficTabFeatureConfig: StatsTrafficTabFeatureConfig
 ) : BaseStatsUseCase<VisitsAndViewsModel, UiState>(
     OVERVIEW,
     mainDispatcher,
@@ -144,6 +150,120 @@ class OverviewUseCase constructor(
         domainModel: VisitsAndViewsModel,
         uiState: UiState
     ): List<BlockListItem> {
+        return if (trafficTabFeatureConfig.isEnabled()) {
+            buildTrafficOverview(domainModel, uiState)
+        } else {
+            buildGranularOverview(domainModel, uiState)
+        }
+    }
+
+    private fun buildTrafficOverview(domainModel: VisitsAndViewsModel, uiState: UiState): List<BlockListItem> {
+        val items = mutableListOf<BlockListItem>()
+        if (domainModel.dates.isNotEmpty()) {
+            val dateFromProvider = selectedDateProvider.getSelectedDate(statsGranularity)
+            val visibleBarCount = uiState.visibleBarCount ?: domainModel.dates.size
+            val availableDates = domainModel.dates.map {
+                statsDateFormatter.parseStatsDate(
+                    statsGranularity,
+                    it.period
+                )
+            }
+            val selectedDate = dateFromProvider ?: availableDates.last()
+            val index = availableDates.indexOf(selectedDate)
+
+            selectedDateProvider.selectDate(
+                selectedDate,
+                availableDates.takeLast(visibleBarCount),
+                statsGranularity
+            )
+            val selectedItem = domainModel.dates.getOrNull(index) ?: domainModel.dates.last()
+            val previousItem = domainModel.dates.getOrNull(domainModel.dates.indexOf(selectedItem) - 1)
+
+            if (statsGranularity == StatsGranularity.DAYS) {
+                buildTodayCard(selectedItem,items)
+            } else {
+                buildGranularChart(domainModel, uiState, items, selectedItem, previousItem)
+            }
+        } else {
+            selectedDateProvider.onDateLoadingFailed(statsGranularity)
+            AppLog.e(T.STATS, "There is no data to be shown in the overview block")
+        }
+        return items
+    }
+
+    private fun buildTodayCard(
+        selectedItem: VisitsAndViewsModel.PeriodData?,
+        items: MutableList<BlockListItem>
+    ) {
+        val views = selectedItem?.views ?: 0
+        val visitors = selectedItem?.visitors ?: 0
+        val likes = selectedItem?.likes ?: 0
+        val comments = selectedItem?.comments ?: 0
+
+        items.add(BlockListItem.Title(R.string.stats_timeframe_today))
+        items.add(
+            QuickScanItem(
+                Column(
+                    R.string.stats_views,
+                    statsUtils.toFormattedString(views)
+                ),
+                Column(
+                    R.string.stats_visitors,
+                    statsUtils.toFormattedString(visitors)
+                )
+            )
+        )
+
+        items.add(
+            QuickScanItem(
+                Column(
+                    R.string.stats_likes,
+                    statsUtils.toFormattedString(likes)
+                ),
+                Column(
+                    R.string.stats_comments,
+                    statsUtils.toFormattedString(comments)
+                )
+            )
+        )
+    }
+
+    private fun buildGranularChart(
+        domainModel: VisitsAndViewsModel,
+        uiState: UiState,
+        items: MutableList<BlockListItem>,
+        selectedItem: VisitsAndViewsModel.PeriodData,
+        previousItem: VisitsAndViewsModel.PeriodData?
+    ) {
+        items.add(
+            overviewMapper.buildTitle(
+                selectedItem,
+                previousItem,
+                uiState.selectedPosition,
+                isLast = selectedItem == domainModel.dates.last(),
+                statsGranularity = statsGranularity
+            )
+        )
+        items.addAll(
+            overviewMapper.buildChart(
+                domainModel.dates,
+                statsGranularity,
+                this::onBarSelected,
+                this::onBarChartDrawn,
+                uiState.selectedPosition,
+                selectedItem.period
+            )
+        )
+        items.add(
+            overviewMapper.buildColumns(
+                selectedItem,
+                this::onColumnSelected,
+                uiState.selectedPosition
+            )
+        )
+    }
+
+    private fun buildGranularOverview(domainModel: VisitsAndViewsModel, uiState: UiState): List<BlockListItem> {
         val items = mutableListOf<BlockListItem>()
         if (domainModel.dates.isNotEmpty()) {
             val dateFromProvider = selectedDateProvider.getSelectedDate(statsGranularity)
@@ -237,7 +357,9 @@ class OverviewUseCase constructor(
         private val analyticsTracker: AnalyticsTrackerWrapper,
         private val statsWidgetUpdaters: StatsWidgetUpdaters,
         private val localeManagerWrapper: LocaleManagerWrapper,
-        private val resourceProvider: ResourceProvider
+        private val resourceProvider: ResourceProvider,
+        private val statsUtils: StatsUtils,
+        private val trafficTabFeatureConfig: StatsTrafficTabFeatureConfig
     ) : GranularUseCaseFactory {
         override fun build(granularity: StatsGranularity, useCaseMode: UseCaseMode) =
             OverviewUseCase(
@@ -252,7 +374,9 @@ class OverviewUseCase constructor(
                 analyticsTracker,
                 statsWidgetUpdaters,
                 localeManagerWrapper,
-                resourceProvider
+                resourceProvider,
+                statsUtils,
+                trafficTabFeatureConfig
             )
     }
 }


Test patch:
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCaseTest.kt
index 3757b6c82b41..d0206175cee8 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCaseTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCaseTest.kt
@@ -35,8 +35,10 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.granular.SelectedDa
 import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetUpdater.StatsWidgetUpdaters
 import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter
 import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider
+import org.wordpress.android.ui.stats.refresh.utils.StatsUtils
 import org.wordpress.android.util.LocaleManagerWrapper
 import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper
+import org.wordpress.android.util.config.StatsTrafficTabFeatureConfig
 import org.wordpress.android.viewmodel.ResourceProvider
 import java.util.Calendar
 
@@ -77,6 +79,13 @@ class OverviewUseCaseTest : BaseUnitTest() {
 
     @Mock
     lateinit var localeManagerWrapper: LocaleManagerWrapper
+
+    @Mock
+    lateinit var statsUtils: StatsUtils
+
+    @Mock
+    lateinit var trafficTabFeatureConfig: StatsTrafficTabFeatureConfig
+
     private lateinit var useCase: OverviewUseCase
     private val site = SiteModel()
     private val siteId = 1L
@@ -100,7 +109,9 @@ class OverviewUseCaseTest : BaseUnitTest() {
             analyticsTrackerWrapper,
             statsWidgetUpdaters,
             localeManagerWrapper,
-            resourceProvider
+            resourceProvider,
+            statsUtils,
+            trafficTabFeatureConfig
         )
         site.siteId = siteId
         whenever(statsSiteProvider.siteModel).thenReturn(site)