fix(#6): address PR review

- matchesQuery: use %.2f format to match the display (was Double.toString, which can use scientific notation and disagree with the row's %.2f)
- Remove unused Switch import in AddEditRecurringScreen (the active toggle uses ToggleItem)
- Extract CategoryDropdownField to ui/components/CategoryDropdownField.kt so both add/edit screens share one implementation; takes label as a parameter
- Remove unused recurring_list_active string
This commit is contained in:
Achmad Setyabudi Susilo
2026-06-28 19:49:02 +07:00
parent b0a62bedf0
commit e8c7a14c75
5 changed files with 66 additions and 106 deletions
@@ -0,0 +1,61 @@
package dev.achmad.ledgerr.ui.components
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuAnchorType
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import dev.achmad.ledgerr.domain.category.model.Category
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CategoryDropdownField(
categoryId: Long?,
categories: List<Category>,
onCategoryChange: (Long) -> Unit,
label: String,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }
val selectedName = categories.firstOrNull { it.id == categoryId }?.name.orEmpty()
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = !expanded },
modifier = modifier,
) {
OutlinedTextField(
value = selectedName,
onValueChange = {},
readOnly = true,
label = { Text(label) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
modifier = Modifier
.fillMaxWidth()
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable, enabled = true),
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
categories.forEach { category ->
DropdownMenuItem(
text = { Text(category.name) },
onClick = {
onCategoryChange(category.id)
expanded = false
},
)
}
}
}
}
@@ -10,11 +10,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuAnchorType
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -22,9 +18,6 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@@ -35,11 +28,10 @@ import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import dev.achmad.ledgerr.R import dev.achmad.ledgerr.R
import dev.achmad.ledgerr.domain.category.model.Category
import dev.achmad.ledgerr.ui.components.AppBar import dev.achmad.ledgerr.ui.components.AppBar
import dev.achmad.ledgerr.ui.components.CategoryDropdownField
import dev.achmad.ledgerr.ui.components.DateField import dev.achmad.ledgerr.ui.components.DateField
import dev.achmad.ledgerr.ui.screens.category.CategoryScreen import dev.achmad.ledgerr.ui.screens.category.CategoryScreen
import java.time.LocalDate
data class AddEditExpenseScreen( data class AddEditExpenseScreen(
val expenseId: Long? = null, val expenseId: Long? = null,
@@ -108,6 +100,7 @@ data class AddEditExpenseScreen(
categoryId = categoryId, categoryId = categoryId,
categories = categories, categories = categories,
onCategoryChange = screenModel::setCategory, onCategoryChange = screenModel::setCategory,
label = stringResource(R.string.add_edit_expense_field_category),
) )
DateField( DateField(
@@ -135,46 +128,3 @@ data class AddEditExpenseScreen(
} }
} }
} }
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun CategoryDropdownField(
categoryId: Long?,
categories: List<Category>,
onCategoryChange: (Long) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }
val selectedName = categories.firstOrNull { it.id == categoryId }?.name.orEmpty()
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = !expanded },
modifier = modifier,
) {
OutlinedTextField(
value = selectedName,
onValueChange = {},
readOnly = true,
label = { Text(stringResource(R.string.add_edit_expense_field_category)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
modifier = Modifier
.fillMaxWidth()
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable, enabled = true),
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
categories.forEach { category ->
DropdownMenuItem(
text = { Text(category.name) },
onClick = {
onCategoryChange(category.id)
expanded = false
},
)
}
}
}
}
@@ -10,24 +10,16 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuAnchorType
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@@ -38,9 +30,9 @@ import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import dev.achmad.ledgerr.R import dev.achmad.ledgerr.R
import dev.achmad.ledgerr.domain.category.model.Category
import dev.achmad.ledgerr.domain.recurring.model.RecurringInterval import dev.achmad.ledgerr.domain.recurring.model.RecurringInterval
import dev.achmad.ledgerr.ui.components.AppBar import dev.achmad.ledgerr.ui.components.AppBar
import dev.achmad.ledgerr.ui.components.CategoryDropdownField
import dev.achmad.ledgerr.ui.components.DateField import dev.achmad.ledgerr.ui.components.DateField
import dev.achmad.ledgerr.ui.components.LabeledRadioButton import dev.achmad.ledgerr.ui.components.LabeledRadioButton
import dev.achmad.ledgerr.ui.components.ToggleItem import dev.achmad.ledgerr.ui.components.ToggleItem
@@ -114,6 +106,7 @@ data class AddEditRecurringScreen(
categoryId = categoryId, categoryId = categoryId,
categories = categories, categories = categories,
onCategoryChange = screenModel::setCategory, onCategoryChange = screenModel::setCategory,
label = stringResource(R.string.add_edit_recurring_field_category),
) )
IntervalSection( IntervalSection(
@@ -148,49 +141,6 @@ data class AddEditRecurringScreen(
} }
} }
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun CategoryDropdownField(
categoryId: Long?,
categories: List<Category>,
onCategoryChange: (Long) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }
val selectedName = categories.firstOrNull { it.id == categoryId }?.name.orEmpty()
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = !expanded },
modifier = modifier,
) {
OutlinedTextField(
value = selectedName,
onValueChange = {},
readOnly = true,
label = { Text(stringResource(R.string.add_edit_recurring_field_category)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
modifier = Modifier
.fillMaxWidth()
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable, enabled = true),
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
categories.forEach { category ->
DropdownMenuItem(
text = { Text(category.name) },
onClick = {
onCategoryChange(category.id)
expanded = false
},
)
}
}
}
}
@Composable @Composable
private fun IntervalSection( private fun IntervalSection(
selected: RecurringInterval, selected: RecurringInterval,
@@ -106,7 +106,7 @@ class ExpenseListScreenModel(
private fun matchesQuery(item: ExpenseWithCategory, query: String): Boolean { private fun matchesQuery(item: ExpenseWithCategory, query: String): Boolean {
if (query.isBlank()) return true if (query.isBlank()) return true
val note = item.expense.note.orEmpty() val note = item.expense.note.orEmpty()
val amountStr = item.expense.amount.toString() val amountStr = "%.2f".format(item.expense.amount)
val categoryName = item.category.name val categoryName = item.category.name
return note.contains(query, ignoreCase = true) || return note.contains(query, ignoreCase = true) ||
amountStr.contains(query) || amountStr.contains(query) ||
-1
View File
@@ -79,7 +79,6 @@
<string name="expense_list_delete_message">This will permanently delete the expense.</string> <string name="expense_list_delete_message">This will permanently delete the expense.</string>
<string name="expense_list_empty">No expenses yet</string> <string name="expense_list_empty">No expenses yet</string>
<string name="recurring_list_empty">No recurring expenses</string> <string name="recurring_list_empty">No recurring expenses</string>
<string name="recurring_list_active">Active</string>
<!-- Add / Edit expense (issue #6) --> <!-- Add / Edit expense (issue #6) -->
<string name="add_edit_expense_title_new">Add Expense</string> <string name="add_edit_expense_title_new">Add Expense</string>