Coverage Summary for Class: DashboardBalanceWidgetKt (com.georgeci.moneysurfer.feature.dashboard)

Class Class, % Method, % Branch, % Line, % Instruction, %
DashboardBalanceWidgetKt 100% (1/1) 100% (3/3) 65% (13/20) 90% (27/30) 87.1% (310/356)


 package com.georgeci.moneysurfer.feature.dashboard
 
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.unit.dp
 import com.georgeci.moneysurfer.uikit.widgets.SurferBalanceFootnote
 import com.georgeci.moneysurfer.uikit.widgets.SurferBalanceTrend
 import com.georgeci.moneysurfer.uikit.widgets.SurferBalanceVariant
 import com.georgeci.moneysurfer.uikit.widgets.SurferBalanceWidget
 import moneysurfer.feature.dashboard.generated.resources.Res
 import moneysurfer.feature.dashboard.generated.resources.dashboard_balance_empty_text
 import moneysurfer.feature.dashboard.generated.resources.dashboard_balance_other_currencies
 import moneysurfer.feature.dashboard.generated.resources.dashboard_balance_rates_as_of
 import moneysurfer.feature.dashboard.generated.resources.dashboard_balance_title
 import moneysurfer.feature.dashboard.generated.resources.dashboard_balance_trend_month
 import org.jetbrains.compose.resources.stringResource
 
 /**
  * The headline total, what qualifies it, and how it got there.
  *
  * Lives beside the other per-widget files rather than in `DashboardScreen` because the card now
  * assembles three separate things out of the state — the figure, one of three footnotes, and the
  * month's movement — and each of them is a decision worth reading on its own.
  *
  * [variant] stays a raw key until it reaches the widget that defines the treatments: see
  * [DashboardWidget].
  */
 @Composable
 internal fun BalanceWidget(state: DashboardState.Content, variant: String?) {
     SurferBalanceWidget(
         title = stringResource(Res.string.dashboard_balance_title),
         balance = state.formattedTotalBalance ?: "—",
         variant = SurferBalanceVariant.fromKey(variant),
         footnote = balanceFootnote(state),
         trend = balanceTrend(state),
         modifier = Modifier
             .fillMaxWidth()
             .padding(horizontal = 16.dp)
             .testTag(DashboardTestTags.Balance),
     )
 }
 
 /**
  * Line under the headline balance. No total at all wins over everything; after that come the two
  * things that qualify the headline — balances no rate could absorb, then how old the rates that
  * built it are. Only one of them fits, and the reader who might misread the figure needs the first.
  *
  * The month delta is not in this list: it lives in [balanceTrend], so a multi-currency workspace
  * gets its trend *and* the "as of" line rather than only the one that sorted higher here.
  */
 @Composable
 private fun balanceFootnote(state: DashboardState.Content): SurferBalanceFootnote? = when {
     state.formattedTotalBalance == null ->
         SurferBalanceFootnote.Empty(stringResource(Res.string.dashboard_balance_empty_text))
     state.otherCurrencyTotals.isNotEmpty() -> SurferBalanceFootnote.Note(
         stringResource(
             Res.string.dashboard_balance_other_currencies,
             state.otherCurrencyTotals.joinToString(" · "),
         ),
     )
     state.ratesAsOf != null -> SurferBalanceFootnote.Note(
         stringResource(Res.string.dashboard_balance_rates_as_of, state.ratesAsOf),
     )
     else -> null
 }
 
 /**
  * The balance card's movement half — the month delta as a sentence, and the curve it moved along.
  * Null when the view model found nothing honest to say about either; the widget then draws the
  * figure alone, as it did before the trend existed.
  */
 @Composable
 private fun balanceTrend(state: DashboardState.Content): SurferBalanceTrend? {
     val delta = state.formattedTrendDelta
     if (delta == null && state.balanceSeries.size < 2) return null
     return SurferBalanceTrend(
         text = delta?.let { stringResource(Res.string.dashboard_balance_trend_month, it) },
         series = state.balanceSeries,
         isNegative = state.isTrendDeltaNegative,
     )
 }