fix: add info text when no data
This commit is contained in:
@@ -49,8 +49,6 @@ abstract class TripDatabase : RoomDatabase() {
|
|||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
object DatabaseModule {
|
object DatabaseModule {
|
||||||
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -140,12 +138,12 @@ private class DatabasePrepopulator(
|
|||||||
currency = "USD"
|
currency = "USD"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
for (category in sampleCategories) {
|
// for (category in sampleCategories) {
|
||||||
categoryDao.insert(category)
|
// categoryDao.insert(category)
|
||||||
}
|
// }
|
||||||
for (expense in sampleExpenses) {
|
// for (expense in sampleExpenses) {
|
||||||
expenseDao.insert(expense)
|
// expenseDao.insert(expense)
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ data class Trip(
|
|||||||
@ColumnInfo("currency") val currency: String,
|
@ColumnInfo("currency") val currency: String,
|
||||||
@ColumnInfo("budget") val budget: Double = 0.0
|
@ColumnInfo("budget") val budget: Double = 0.0
|
||||||
) {
|
) {
|
||||||
|
fun isDummy(): Boolean {
|
||||||
|
return this.id == -1
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
val DUMMY = Trip(
|
val DUMMY = Trip(
|
||||||
|
|||||||
@@ -115,9 +115,9 @@ fun AddExpenseBottomSheet(
|
|||||||
) {
|
) {
|
||||||
val currentTripId = currentTrip.id
|
val currentTripId = currentTrip.id
|
||||||
|
|
||||||
if (categories.isEmpty()) {
|
// if (categories.isEmpty()) {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
var amount by remember {
|
var amount by remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
@@ -138,7 +138,11 @@ fun AddExpenseBottomSheet(
|
|||||||
expenseDtoToEdit?.expense?.currency ?: currentTrip.currency
|
expenseDtoToEdit?.expense?.currency ?: currentTrip.currency
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
var category by remember { mutableStateOf(expenseDtoToEdit?.category ?: categories[0]) }
|
var category by remember {
|
||||||
|
mutableStateOf<Category?>(
|
||||||
|
expenseDtoToEdit?.category ?: if (categories.isEmpty()) null else categories[0]
|
||||||
|
)
|
||||||
|
}
|
||||||
var datetime by remember {
|
var datetime by remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
expenseDtoToEdit?.expense?.datetime ?: LocalDateTime.now()
|
expenseDtoToEdit?.expense?.datetime ?: LocalDateTime.now()
|
||||||
@@ -273,14 +277,14 @@ fun AddExpenseBottomSheet(
|
|||||||
|
|
||||||
SaveButton(
|
SaveButton(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
enabled = enableSave,
|
enabled = enableSave && category != null,
|
||||||
onClick = {
|
onClick = {
|
||||||
val expenseToSave = Expense(
|
val expenseToSave = Expense(
|
||||||
amount = equationResult,
|
amount = equationResult,
|
||||||
currency = currency,
|
currency = currency,
|
||||||
note = note,
|
note = note,
|
||||||
datetime = datetime,
|
datetime = datetime,
|
||||||
categoryId = category.id,
|
categoryId = category!!.id,
|
||||||
tripId = currentTripId
|
tripId = currentTripId
|
||||||
)
|
)
|
||||||
onSave(
|
onSave(
|
||||||
@@ -410,7 +414,7 @@ fun CurrencyButton(modifier: Modifier = Modifier, onClick: () -> Unit, text: Str
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CategoryButton(onClick: () -> Unit, category: Category, modifier: Modifier = Modifier) {
|
fun CategoryButton(onClick: () -> Unit, category: Category?, modifier: Modifier = Modifier) {
|
||||||
Button(
|
Button(
|
||||||
contentPadding = PaddingValues(0.dp),
|
contentPadding = PaddingValues(0.dp),
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
@@ -422,25 +426,21 @@ fun CategoryButton(onClick: () -> Unit, category: Category, modifier: Modifier =
|
|||||||
contentColor = MaterialTheme.colorScheme.onPrimary
|
contentColor = MaterialTheme.colorScheme.onPrimary
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
// Row(modifier = modifier.fillMaxWidth()) {
|
if (category != null) {
|
||||||
Icon(
|
Icon(
|
||||||
tint = Color(category.color.toColorInt()),
|
tint = Color(category.color.toColorInt()),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(30.dp)
|
.size(30.dp)
|
||||||
// .background(
|
.padding(end = 10.dp),
|
||||||
// color = MaterialTheme.colorScheme.prima,
|
painter = painterResource(category.icon.resource),
|
||||||
// shape = MaterialTheme.shapes.small
|
contentDescription = stringResource(R.string.category),
|
||||||
// )
|
)
|
||||||
.padding(end = 10.dp),
|
}
|
||||||
painter = painterResource(category.icon.resource),
|
|
||||||
contentDescription = stringResource(R.string.category),
|
|
||||||
)
|
|
||||||
Text(
|
Text(
|
||||||
text = category.name,
|
text = category?.name ?: stringResource(R.string.pick_category),
|
||||||
style = MaterialTheme.typography.titleMedium
|
style = MaterialTheme.typography.titleMedium
|
||||||
)
|
)
|
||||||
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.graphics.toColorInt
|
import androidx.core.graphics.toColorInt
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import cc.n0th1ng.tripmoney.R.*
|
import cc.n0th1ng.tripmoney.R.string
|
||||||
import cc.n0th1ng.tripmoney.data.entity.Category
|
import cc.n0th1ng.tripmoney.data.entity.Category
|
||||||
import cc.n0th1ng.tripmoney.screens.AddCategoryDialog
|
import cc.n0th1ng.tripmoney.screens.AddCategoryDialog
|
||||||
import cc.n0th1ng.tripmoney.viewmodel.ExpenseAndCategoryViewModel
|
import cc.n0th1ng.tripmoney.viewmodel.ExpenseAndCategoryViewModel
|
||||||
@@ -35,7 +35,7 @@ import com.composables.icons.materialsymbols.outlined.R
|
|||||||
fun CategorySelectionDialog(
|
fun CategorySelectionDialog(
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onCategorySelected: (Category) -> Unit,
|
onCategorySelected: (Category) -> Unit,
|
||||||
selected: Category,
|
selected: Category?,
|
||||||
categories: List<Category>,
|
categories: List<Category>,
|
||||||
) {
|
) {
|
||||||
val expenseAndCategoryViewModel: ExpenseAndCategoryViewModel = hiltViewModel()
|
val expenseAndCategoryViewModel: ExpenseAndCategoryViewModel = hiltViewModel()
|
||||||
|
|||||||
@@ -47,11 +47,12 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.testTag
|
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.contentDescription
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
import androidx.compose.ui.semantics.semantics
|
import androidx.compose.ui.semantics.semantics
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.graphics.toColorInt
|
import androidx.core.graphics.toColorInt
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
@@ -100,6 +101,7 @@ fun ListExpenseScreen(
|
|||||||
val isRecalculatingRate by tripViewModel.isRecalculating.collectAsState()
|
val isRecalculatingRate by tripViewModel.isRecalculating.collectAsState()
|
||||||
|
|
||||||
ListExpenseScreen(
|
ListExpenseScreen(
|
||||||
|
currentTrip = currentTrip,
|
||||||
expensesFlow = expensesFlow,
|
expensesFlow = expensesFlow,
|
||||||
onSaveExpense = { expenseAndCategoryViewModel.save(it, currentTrip!!) },
|
onSaveExpense = { expenseAndCategoryViewModel.save(it, currentTrip!!) },
|
||||||
onDeleteExpense = { expenseAndCategoryViewModel.delete(it) },
|
onDeleteExpense = { expenseAndCategoryViewModel.delete(it) },
|
||||||
@@ -114,6 +116,7 @@ fun ListExpenseScreen(
|
|||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
@Composable
|
@Composable
|
||||||
fun ListExpenseScreen(
|
fun ListExpenseScreen(
|
||||||
|
currentTrip: Trip?,
|
||||||
expensesFlow: Flow<PagingData<ExpenseListItemUi>>,
|
expensesFlow: Flow<PagingData<ExpenseListItemUi>>,
|
||||||
onSaveExpense: (Expense) -> Unit, onDeleteExpense: (Expense) -> Unit,
|
onSaveExpense: (Expense) -> Unit, onDeleteExpense: (Expense) -> Unit,
|
||||||
isRecalculatingRate: Boolean,
|
isRecalculatingRate: Boolean,
|
||||||
@@ -137,60 +140,83 @@ fun ListExpenseScreen(
|
|||||||
var itemToDelete by remember { mutableStateOf<Expense?>(null) }
|
var itemToDelete by remember { mutableStateOf<Expense?>(null) }
|
||||||
|
|
||||||
Scaffold(floatingActionButtonPosition = FabPosition.EndOverlay, floatingActionButton = {
|
Scaffold(floatingActionButtonPosition = FabPosition.EndOverlay, floatingActionButton = {
|
||||||
ExtendedFloatingActionButton(
|
if (currentTrip != null && !currentTrip.isDummy()) {
|
||||||
onClick = { showBottomSheet = true },
|
ExtendedFloatingActionButton(
|
||||||
icon = { Icon(Icons.Filled.Add, stringResource(string.add_expense)) },
|
onClick = { showBottomSheet = true },
|
||||||
text = { Text(text = stringResource(string.add_expense)) },
|
icon = { Icon(Icons.Filled.Add, stringResource(string.add_expense)) },
|
||||||
)
|
text = { Text(text = stringResource(string.add_expense)) },
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
Box {
|
Box {
|
||||||
LazyColumn(
|
if (items.itemCount == 0) {
|
||||||
modifier = Modifier.fillMaxSize().semantics {
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
contentDescription = "expensesList"
|
val textToShow = if (currentTrip == null || currentTrip.isDummy()) {
|
||||||
},
|
stringResource(string.no_trip_picked)
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
} else {
|
||||||
state = listState
|
stringResource(string.no_expenses)
|
||||||
) {
|
|
||||||
items(
|
|
||||||
count = items.itemCount,
|
|
||||||
key = items.itemKey { item ->
|
|
||||||
when (item) {
|
|
||||||
is ExpenseListItemUi.Item -> item.expenseDto.expense.id
|
|
||||||
is ExpenseListItemUi.Header -> "header_${item.date}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
) { index ->
|
Text(
|
||||||
|
text = textToShow,
|
||||||
when (val item = items[index]) {
|
textAlign = TextAlign.Center,
|
||||||
|
fontWeight = FontWeight.Light,
|
||||||
is ExpenseListItemUi.Header -> {
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
CustomDivider(
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f)
|
||||||
date = item.date,
|
)
|
||||||
sum = item.sum,
|
|
||||||
currency = item.currency
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
is ExpenseListItemUi.Item -> {
|
|
||||||
SwipeToDeleteExpenseCard(
|
|
||||||
expenseDto = item.expenseDto,
|
|
||||||
onDelete = { expense -> itemToDelete = expense },
|
|
||||||
onClick = { expenseDto ->
|
|
||||||
expenseDtoToEdit = expenseDto
|
|
||||||
showBottomSheet = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
null -> {}
|
|
||||||
|
|
||||||
}
|
|
||||||
Spacer(Modifier.height(10.dp))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.semantics {
|
||||||
|
contentDescription = "expensesList"
|
||||||
|
},
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
state = listState
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
count = items.itemCount,
|
||||||
|
key = items.itemKey { item ->
|
||||||
|
when (item) {
|
||||||
|
is ExpenseListItemUi.Item -> item.expenseDto.expense.id
|
||||||
|
is ExpenseListItemUi.Header -> "header_${item.date}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { index ->
|
||||||
|
|
||||||
|
when (val item = items[index]) {
|
||||||
|
|
||||||
|
is ExpenseListItemUi.Header -> {
|
||||||
|
CustomDivider(
|
||||||
|
date = item.date,
|
||||||
|
sum = item.sum,
|
||||||
|
currency = item.currency
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ExpenseListItemUi.Item -> {
|
||||||
|
SwipeToDeleteExpenseCard(
|
||||||
|
expenseDto = item.expenseDto,
|
||||||
|
onDelete = { expense -> itemToDelete = expense },
|
||||||
|
onClick = { expenseDto ->
|
||||||
|
expenseDtoToEdit = expenseDto
|
||||||
|
showBottomSheet = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
null -> {}
|
||||||
|
|
||||||
|
}
|
||||||
|
Spacer(Modifier.height(10.dp))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemToDelete != null) {
|
if (itemToDelete != null) {
|
||||||
@@ -466,6 +492,57 @@ fun PreviewListExpenseScreen() {
|
|||||||
TripMoneyTheme() {
|
TripMoneyTheme() {
|
||||||
val pagingData = PagingData.from(sampleExpenseDtoWithConvertedAmountList())
|
val pagingData = PagingData.from(sampleExpenseDtoWithConvertedAmountList())
|
||||||
ListExpenseScreen(
|
ListExpenseScreen(
|
||||||
|
currentTrip = Trip(
|
||||||
|
id = 1,
|
||||||
|
name = "Vacation",
|
||||||
|
currency = "USD",
|
||||||
|
startDate = LocalDate.parse("2026-01-01"),
|
||||||
|
endDate = LocalDate.parse("2026-01-11"),
|
||||||
|
),
|
||||||
|
expensesFlow = MutableStateFlow(pagingData),
|
||||||
|
onSaveExpense = {},
|
||||||
|
onDeleteExpense = {},
|
||||||
|
isRecalculatingRate = true,
|
||||||
|
false,
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
@AllPreviews
|
||||||
|
@Composable
|
||||||
|
fun PreviewListExpenseScreenWithoutExpenses() {
|
||||||
|
TripMoneyTheme() {
|
||||||
|
val pagingData = PagingData.from(emptyList<ExpenseListItemUi>())
|
||||||
|
ListExpenseScreen(
|
||||||
|
currentTrip = Trip(
|
||||||
|
id = 1,
|
||||||
|
name = "Vacation",
|
||||||
|
currency = "USD",
|
||||||
|
startDate = LocalDate.parse("2026-01-01"),
|
||||||
|
endDate = LocalDate.parse("2026-01-11"),
|
||||||
|
),
|
||||||
|
expensesFlow = MutableStateFlow(pagingData),
|
||||||
|
onSaveExpense = {},
|
||||||
|
onDeleteExpense = {},
|
||||||
|
isRecalculatingRate = true,
|
||||||
|
false,
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
@AllPreviews
|
||||||
|
@Composable
|
||||||
|
fun PreviewListExpenseScreenWithoutTrip() {
|
||||||
|
TripMoneyTheme() {
|
||||||
|
val pagingData = PagingData.from(emptyList<ExpenseListItemUi>())
|
||||||
|
ListExpenseScreen(
|
||||||
|
currentTrip = null,
|
||||||
expensesFlow = MutableStateFlow(pagingData),
|
expensesFlow = MutableStateFlow(pagingData),
|
||||||
onSaveExpense = {},
|
onSaveExpense = {},
|
||||||
onDeleteExpense = {},
|
onDeleteExpense = {},
|
||||||
@@ -480,7 +557,7 @@ fun PreviewListExpenseScreen() {
|
|||||||
@AllPreviews
|
@AllPreviews
|
||||||
@Composable
|
@Composable
|
||||||
fun PreviewDeleteConfirmationDialog() {
|
fun PreviewDeleteConfirmationDialog() {
|
||||||
TripMoneyTheme() {
|
TripMoneyTheme {
|
||||||
DeleteConfirmationDialog(
|
DeleteConfirmationDialog(
|
||||||
onConfirm = {},
|
onConfirm = {},
|
||||||
onCancel = {})
|
onCancel = {})
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.res.colorResource
|
import androidx.compose.ui.res.colorResource
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.graphics.toColorInt
|
import androidx.core.graphics.toColorInt
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
@@ -85,7 +87,9 @@ fun StatisticsScreen(
|
|||||||
) {
|
) {
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||||
Summary(
|
Summary(
|
||||||
Modifier.weight(1f), -1 * summaryAmount, tripCurrency.name,
|
Modifier.weight(1f),
|
||||||
|
if (summaryAmount == 0.0) 0.0 else -1 * summaryAmount,
|
||||||
|
tripCurrency.name,
|
||||||
stringResource(cc.n0th1ng.tripmoney.R.string.total_expenses),
|
stringResource(cc.n0th1ng.tripmoney.R.string.total_expenses),
|
||||||
R.drawable.materialsymbols_ic_payment_arrow_down_outlined,
|
R.drawable.materialsymbols_ic_payment_arrow_down_outlined,
|
||||||
iconColor = MaterialTheme.colorScheme.error
|
iconColor = MaterialTheme.colorScheme.error
|
||||||
@@ -159,19 +163,31 @@ fun SummaryPerCategoryCard(summaryPerCategoryList: List<SummaryPerCategory>) {
|
|||||||
colors = CardDefaults.elevatedCardColors()
|
colors = CardDefaults.elevatedCardColors()
|
||||||
.copy(containerColor = MaterialTheme.colorScheme.surfaceContainer)
|
.copy(containerColor = MaterialTheme.colorScheme.surfaceContainer)
|
||||||
) {
|
) {
|
||||||
Column(
|
if (summaryPerCategoryList.isEmpty()) {
|
||||||
modifier = Modifier
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
.padding(15.dp)
|
Text(
|
||||||
.verticalScroll(rememberScrollState()),
|
text = stringResource(cc.n0th1ng.tripmoney.R.string.no_expenses_summary),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
verticalArrangement = Arrangement.spacedBy(5.dp)
|
fontWeight = FontWeight.Light,
|
||||||
) {
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
summaryPerCategoryList.forEach {
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f)
|
||||||
CategoryCard(
|
|
||||||
summaryPerCategory = it, modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(15.dp)
|
||||||
|
.verticalScroll(rememberScrollState()),
|
||||||
|
|
||||||
|
verticalArrangement = Arrangement.spacedBy(5.dp)
|
||||||
|
) {
|
||||||
|
summaryPerCategoryList.forEach {
|
||||||
|
CategoryCard(
|
||||||
|
summaryPerCategory = it, modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,7 +254,7 @@ fun CategoryCard(modifier: Modifier = Modifier, summaryPerCategory: SummaryPerCa
|
|||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
@AllPreviews
|
@AllPreviews
|
||||||
@Composable
|
@Composable
|
||||||
fun Preview() {
|
fun PreviewStatisticScreen() {
|
||||||
TripMoneyTheme {
|
TripMoneyTheme {
|
||||||
Scaffold {
|
Scaffold {
|
||||||
StatisticsScreen(
|
StatisticsScreen(
|
||||||
@@ -251,6 +267,25 @@ fun Preview() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
@AllPreviews
|
||||||
|
@Composable
|
||||||
|
fun PreviewStatisticScreenWithNoData() {
|
||||||
|
TripMoneyTheme {
|
||||||
|
Scaffold {
|
||||||
|
StatisticsScreen(
|
||||||
|
emptyList(),
|
||||||
|
summaryAmount = 0.0,
|
||||||
|
Currencies.entries.random(),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val categories = listOf(
|
val categories = listOf(
|
||||||
Category(name = "Jedzenie", icon = Icons.RESTAURANT, color = colors.random()),
|
Category(name = "Jedzenie", icon = Icons.RESTAURANT, color = colors.random()),
|
||||||
Category(name = "Transport", icon = Icons.FLIGHT, color = colors.random()),
|
Category(name = "Transport", icon = Icons.FLIGHT, color = colors.random()),
|
||||||
|
|||||||
@@ -38,14 +38,13 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
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.draw.alpha
|
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
@@ -113,32 +112,45 @@ fun TripPickerScreen(
|
|||||||
Icon(Icons.Filled.Add, stringResource(string.add_trip))
|
Icon(Icons.Filled.Add, stringResource(string.add_trip))
|
||||||
}
|
}
|
||||||
}) { paddingValues ->
|
}) { paddingValues ->
|
||||||
LazyColumn(
|
if (trips.itemCount == 0) {
|
||||||
modifier = Modifier
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
.padding(horizontal = 15.dp)
|
Text(
|
||||||
.fillMaxSize(),
|
text = stringResource(string.no_trip_added),
|
||||||
verticalArrangement = Arrangement.Center
|
textAlign = TextAlign.Center,
|
||||||
) {
|
fontWeight = FontWeight.Light,
|
||||||
items(trips.itemCount, trips.itemKey { it.id }) { i ->
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
Spacer(Modifier.height(10.dp))
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f)
|
||||||
val trip = trips[i]
|
)
|
||||||
if (trip != null) {
|
}
|
||||||
SwipeToDeleteTripCard(
|
} else {
|
||||||
trip = trip,
|
LazyColumn(
|
||||||
onDelete = {
|
modifier = Modifier
|
||||||
onDelete(trip)
|
.padding(horizontal = 15.dp)
|
||||||
}, onClick = {
|
.fillMaxSize(),
|
||||||
onClick(trip)
|
verticalArrangement = Arrangement.Center
|
||||||
}, isSelected = currentTripId == trip.id,
|
) {
|
||||||
onLongClick = { trip ->
|
items(trips.itemCount, trips.itemKey { it.id }) { i ->
|
||||||
tripToEdit = trip
|
Spacer(Modifier.height(10.dp))
|
||||||
showBottomSheet = true
|
val trip = trips[i]
|
||||||
})
|
if (trip != null) {
|
||||||
|
SwipeToDeleteTripCard(
|
||||||
|
trip = trip,
|
||||||
|
onDelete = {
|
||||||
|
onDelete(trip)
|
||||||
|
}, onClick = {
|
||||||
|
onClick(trip)
|
||||||
|
}, isSelected = currentTripId == trip.id,
|
||||||
|
onLongClick = { trip ->
|
||||||
|
tripToEdit = trip
|
||||||
|
showBottomSheet = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Spacer(Modifier.height(10.dp))
|
||||||
}
|
}
|
||||||
Spacer(Modifier.height(10.dp))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (showBottomSheet) {
|
if (showBottomSheet) {
|
||||||
AddTripBottomSheet(
|
AddTripBottomSheet(
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
@@ -250,7 +262,8 @@ fun TripCard(
|
|||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(end = 20.dp),
|
modifier = Modifier.padding(end = 20.dp),
|
||||||
horizontalAlignment = Alignment.End) {
|
horizontalAlignment = Alignment.End
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
trip.currency.uppercase(),
|
trip.currency.uppercase(),
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
@@ -307,4 +320,20 @@ fun PreviewTripPickerScreen() {
|
|||||||
onSave = {}
|
onSave = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
@AllPreviews
|
||||||
|
@Composable
|
||||||
|
fun PreviewTripPickerScreenNoTrip() {
|
||||||
|
|
||||||
|
TripMoneyTheme {
|
||||||
|
TripPickerScreen(
|
||||||
|
tripsFlow = MutableStateFlow(PagingData.from(emptyList())),
|
||||||
|
currentTripId = 1,
|
||||||
|
onDelete = {},
|
||||||
|
onClick = {},
|
||||||
|
onSave = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -41,4 +41,8 @@
|
|||||||
<string name="add_expense_settings">Open add expense form on startup</string>
|
<string name="add_expense_settings">Open add expense form on startup</string>
|
||||||
<string name="yesterday">Yesterday</string>
|
<string name="yesterday">Yesterday</string>
|
||||||
<string name="clear">Clear</string>
|
<string name="clear">Clear</string>
|
||||||
|
<string name="no_expenses">Start budgeting by adding expenses</string>
|
||||||
|
<string name="no_trip_picked">Select trip to see expenses</string>
|
||||||
|
<string name="no_trip_added">Start budgeting by adding your trip</string>
|
||||||
|
<string name="no_expenses_summary">No expenses to summarize</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user