Coverage Summary for Class: SurferGoalContributionRowKt (com.georgeci.moneysurfer.uikit.components.goal)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| SurferGoalContributionRowKt |
100%
(1/1)
|
50%
(1/2)
|
50%
(16/32)
|
100%
(49/49)
|
91.9%
(525/571)
|
package com.georgeci.moneysurfer.uikit.components.goal
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDownward
import androidx.compose.material.icons.filled.ArrowUpward
import androidx.compose.material.icons.filled.Autorenew
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.georgeci.moneysurfer.uikit.preview.SurferComponentPreview
import com.georgeci.moneysurfer.uikit.semantics.SurferSemantics
import com.georgeci.moneysurfer.uikit.theme.AppTheme
/**
* One line of a goal's contribution history. A withdrawal is a contribution with a negative
* amount, so it differs only in its icon and amount colour.
*
* [automatic] styles a row created by a scheduled auto-contribution. Nothing produces those in
* v1 (autopay is deferred), but the styling belongs with the component rather than with the
* feature that will later switch it on.
*/
@Suppress("LongParameterList")
@Composable
fun SurferGoalContributionRow(
amountLabel: String,
dateLabel: String,
isWithdrawal: Boolean,
modifier: Modifier = Modifier,
note: String? = null,
automatic: Boolean = false,
onClick: (() -> Unit)? = null,
) {
val colors = AppTheme.materialColors
val accent = if (isWithdrawal) colors.error else AppTheme.semanticColors.income
val rowModifier = if (onClick != null) modifier.clickable(onClick = onClick) else modifier
Row(
modifier = rowModifier
.fillMaxWidth()
.padding(vertical = AppTheme.spacing.small),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(AppTheme.spacing.medium),
) {
Box(
modifier = Modifier
.size(36.dp)
.clip(CircleShape)
.background(accent.copy(alpha = ICON_BACKGROUND_ALPHA)),
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = when {
automatic -> Icons.Default.Autorenew
isWithdrawal -> Icons.Default.ArrowUpward
else -> Icons.Default.ArrowDownward
},
contentDescription = SurferSemantics.Decorative,
tint = accent,
modifier = Modifier.size(18.dp),
)
}
Column(modifier = Modifier.weight(1f)) {
Text(
text = note?.takeIf { it.isNotBlank() } ?: dateLabel,
style = AppTheme.typography.bodyMedium,
color = colors.onSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
if (!note.isNullOrBlank()) {
Text(
text = dateLabel,
style = AppTheme.typography.bodySmall,
color = colors.onSurfaceVariant,
)
}
}
Text(
text = amountLabel,
style = AppTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Medium),
color = accent,
)
}
}
private const val ICON_BACKGROUND_ALPHA = 0.12f
@Preview
@Composable
private fun SurferGoalContributionRowPreview() {
SurferComponentPreview {
Column(modifier = Modifier.padding(16.dp)) {
SurferGoalContributionRow(
amountLabel = "+€250",
dateLabel = "12 Jul 2026",
isWithdrawal = false,
note = "Salary top-up",
)
SurferGoalContributionRow(
amountLabel = "−€80",
dateLabel = "3 Jul 2026",
isWithdrawal = true,
)
SurferGoalContributionRow(
amountLabel = "+€100",
dateLabel = "1 Jul 2026",
isWithdrawal = false,
automatic = true,
note = "Monthly autopay",
)
}
}
}