feat: add categories per day stats

This commit is contained in:
Rafal Wisniewski
2026-05-01 15:13:58 +02:00
parent 3286bcf87a
commit cc10ddabbe
9 changed files with 218 additions and 34 deletions

View File

@@ -138,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)
// } }
} }

View File

@@ -2,7 +2,7 @@ package cc.n0th1ng.tripmoney.data.dto
import cc.n0th1ng.tripmoney.data.entity.Category import cc.n0th1ng.tripmoney.data.entity.Category
import cc.n0th1ng.tripmoney.utils.Currencies import cc.n0th1ng.tripmoney.utils.Currencies
import cc.n0th1ng.tripmoney.utils.Icons import java.time.LocalDate
data class SummaryPerCategory( data class SummaryPerCategory(
val category: Category, val category: Category,
@@ -11,12 +11,8 @@ data class SummaryPerCategory(
val currency: Currencies val currency: Currencies
) )
data class SummaryPerCategoryRaw( data class SummaryPerDay(
val categoryId: Int, val day: LocalDate,
val categoryName: String,
val icon: Icons,
val color: String,
val amount: Double, val amount: Double,
val currency: String val percent: Float
) )

View File

@@ -151,7 +151,7 @@ fun ListExpenseScreen(
{ {
Box { Box {
if (items.itemCount == 0) { if (items.itemCount == 0) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxSize().padding(10.dp), contentAlignment = Alignment.Center) {
val textToShow = if (currentTrip == null || currentTrip.isDummy()) { val textToShow = if (currentTrip == null || currentTrip.isDummy()) {
stringResource(string.no_trip_picked) stringResource(string.no_trip_picked)
} else { } else {

View File

@@ -4,7 +4,6 @@ import android.annotation.SuppressLint
import android.os.Build import android.os.Build
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -20,7 +19,6 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
@@ -48,7 +46,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
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.layout.ModifierLocalBeyondBoundsLayout
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.unit.dp import androidx.compose.ui.unit.dp
@@ -63,9 +60,6 @@ import cc.n0th1ng.tripmoney.utils.AllPreviews
import cc.n0th1ng.tripmoney.utils.colors import cc.n0th1ng.tripmoney.utils.colors
import cc.n0th1ng.tripmoney.viewmodel.ExpenseAndCategoryViewModel import cc.n0th1ng.tripmoney.viewmodel.ExpenseAndCategoryViewModel
import com.composables.icons.materialsymbols.outlined.R import com.composables.icons.materialsymbols.outlined.R
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import kotlin.collections.emptyList
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
@Composable @Composable
@@ -159,7 +153,7 @@ fun ManageCategoriesScreen(
if (itemToDelete != null) { if (itemToDelete != null) {
DeleteConfirmationDialog( DeleteConfirmationDialog(
bodyText = stringResource(string.delete_category_info), bodyText = stringResource(string.delete_category_info).format(itemToDelete?.name),
onConfirm = { onConfirm = {
onDeleteCategory(itemToDelete!!) onDeleteCategory(itemToDelete!!)
itemToDelete = null itemToDelete = null

View File

@@ -4,15 +4,19 @@ import android.annotation.SuppressLint
import android.os.Build import android.os.Build
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@@ -35,9 +39,11 @@ 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.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
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.data.dto.SummaryPerCategory import cc.n0th1ng.tripmoney.data.dto.SummaryPerCategory
import cc.n0th1ng.tripmoney.data.dto.SummaryPerDay
import cc.n0th1ng.tripmoney.data.entity.Category import cc.n0th1ng.tripmoney.data.entity.Category
import cc.n0th1ng.tripmoney.data.entity.Trip import cc.n0th1ng.tripmoney.data.entity.Trip
import cc.n0th1ng.tripmoney.theme.TripMoneyTheme import cc.n0th1ng.tripmoney.theme.TripMoneyTheme
@@ -49,6 +55,8 @@ import cc.n0th1ng.tripmoney.viewmodel.ExpenseAndCategoryViewModel
import cc.n0th1ng.tripmoney.viewmodel.SettingsViewModel import cc.n0th1ng.tripmoney.viewmodel.SettingsViewModel
import cc.n0th1ng.tripmoney.viewmodel.TripViewModel import cc.n0th1ng.tripmoney.viewmodel.TripViewModel
import com.composables.icons.materialsymbols.outlined.R import com.composables.icons.materialsymbols.outlined.R
import java.time.LocalDate
import java.time.format.DateTimeFormatter
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
@Composable @Composable
@@ -60,11 +68,14 @@ fun StatisticsScreen() {
val currentTrip by tripViewModel.getTrip(currentTripId).collectAsState(Trip.DUMMY) val currentTrip by tripViewModel.getTrip(currentTripId).collectAsState(Trip.DUMMY)
val summaryPerCategoryList by expenseAndCategoryViewModel.getSummaryPerCategory(currentTripId) val summaryPerCategoryList by expenseAndCategoryViewModel.getSummaryPerCategory(currentTripId)
.collectAsState(emptyList()) .collectAsState(emptyList())
val summaryPerDayList by expenseAndCategoryViewModel.getSummaryPerDay(currentTripId)
.collectAsState(emptyList())
val summaryAmount by expenseAndCategoryViewModel.getSummaryAmount(currentTripId) val summaryAmount by expenseAndCategoryViewModel.getSummaryAmount(currentTripId)
.collectAsState(0.0) .collectAsState(0.0)
val moneyLeft by expenseAndCategoryViewModel.getBudgetLeft(currentTripId).collectAsState(null) val moneyLeft by expenseAndCategoryViewModel.getBudgetLeft(currentTripId).collectAsState(null)
StatisticsScreen( StatisticsScreen(
summaryPerCategoryList, summaryPerCategoryList,
summaryPerDayList,
summaryAmount, summaryAmount,
Currencies.valueOf(currentTrip?.currency ?: Currencies.default().name), Currencies.valueOf(currentTrip?.currency ?: Currencies.default().name),
moneyLeft moneyLeft
@@ -75,6 +86,7 @@ fun StatisticsScreen() {
@Composable @Composable
fun StatisticsScreen( fun StatisticsScreen(
summaryPerCategoryList: List<SummaryPerCategory>, summaryPerCategoryList: List<SummaryPerCategory>,
summaryPerDayList: List<SummaryPerDay>,
summaryAmount: Double, summaryAmount: Double,
tripCurrency: Currencies, tripCurrency: Currencies,
moneyLeft: Double? moneyLeft: Double?
@@ -101,8 +113,11 @@ fun StatisticsScreen(
iconColor = colorResource(cc.n0th1ng.tripmoney.R.color.good_green) iconColor = colorResource(cc.n0th1ng.tripmoney.R.color.good_green)
) )
} }
SummaryPerCategoryCard(summaryPerCategoryList) SummaryPerCategoryCard(
modifier = Modifier.heightIn(max = 300.dp),
summaryPerCategoryList = summaryPerCategoryList
)
SummaryPerDayCard(modifier = Modifier.height(300.dp), summaryPerDayList = summaryPerDayList)
} }
} }
@@ -157,14 +172,22 @@ fun Summary(
} }
@Composable @Composable
fun SummaryPerCategoryCard(summaryPerCategoryList: List<SummaryPerCategory>) { fun SummaryPerCategoryCard(
summaryPerCategoryList: List<SummaryPerCategory>,
modifier: Modifier = Modifier
) {
ElevatedCard( ElevatedCard(
modifier = Modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
colors = CardDefaults.elevatedCardColors() colors = CardDefaults.elevatedCardColors()
.copy(containerColor = MaterialTheme.colorScheme.surfaceContainer) .copy(containerColor = MaterialTheme.colorScheme.surfaceContainer)
) { ) {
if (summaryPerCategoryList.isEmpty()) { if (summaryPerCategoryList.isEmpty()) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(
modifier = Modifier
.fillMaxSize()
.padding(10.dp),
contentAlignment = Alignment.Center
) {
Text( Text(
text = stringResource(cc.n0th1ng.tripmoney.R.string.no_expenses_summary), text = stringResource(cc.n0th1ng.tripmoney.R.string.no_expenses_summary),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
@@ -192,6 +215,47 @@ fun SummaryPerCategoryCard(summaryPerCategoryList: List<SummaryPerCategory>) {
} }
} }
@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun SummaryPerDayCard(modifier: Modifier = Modifier, summaryPerDayList: List<SummaryPerDay>) {
ElevatedCard(
modifier = modifier
.fillMaxWidth(),
colors = CardDefaults.elevatedCardColors()
.copy(containerColor = MaterialTheme.colorScheme.surfaceContainer)
) {
if (summaryPerDayList.isEmpty()) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(10.dp),
contentAlignment = Alignment.Center
) {
Text(
text = stringResource(cc.n0th1ng.tripmoney.R.string.no_expenses_summary),
textAlign = TextAlign.Center,
fontWeight = FontWeight.Light,
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f)
)
}
} else {
Row(
modifier = Modifier
.padding(15.dp)
.horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(5.dp)
) {
summaryPerDayList.forEach {
DayCard(
summaryPerDay = it
)
}
}
}
}
}
@Composable @Composable
fun CategoryCard(modifier: Modifier = Modifier, summaryPerCategory: SummaryPerCategory) { fun CategoryCard(modifier: Modifier = Modifier, summaryPerCategory: SummaryPerCategory) {
Column(modifier = modifier) { Column(modifier = modifier) {
@@ -250,6 +314,67 @@ fun CategoryCard(modifier: Modifier = Modifier, summaryPerCategory: SummaryPerCa
} }
} }
@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun DayCard(modifier: Modifier = Modifier, summaryPerDay: SummaryPerDay) {
Column(
modifier = modifier.fillMaxHeight(), verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "%.2f".format(summaryPerDay.amount),
style = MaterialTheme.typography.labelSmall,
fontSize = (MaterialTheme.typography.labelSmall.fontSize.value - 2).sp,
)
val width = 45.dp
Box(
modifier = Modifier
.width(width)
.fillMaxHeight(0.2f + (0.98f - 0.2f) * summaryPerDay.percent)
.clip(RoundedCornerShape(width / 2))
.background(MaterialTheme.colorScheme.primary)
.padding(top = 5.dp)
) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.size(width - 10.dp)
.background(
MaterialTheme.colorScheme.tertiaryContainer,
shape = RoundedCornerShape(width / 2)
)
.padding(vertical = 3.dp),
) {
Text(
style = MaterialTheme.typography.labelSmall,
textAlign = TextAlign.Center,
lineHeight = 10.sp,
color = MaterialTheme.colorScheme.onTertiaryContainer,
text = summaryPerDay.day.format(DateTimeFormatter.ofPattern("dd"))
)
Text(
style = MaterialTheme.typography.labelSmall,
fontWeight = FontWeight.Light,
fontSize = (MaterialTheme.typography.labelSmall.fontSize.value - 2).sp,
lineHeight = 10.sp,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onTertiaryContainer,
text = summaryPerDay.day.format(DateTimeFormatter.ofPattern("E"))
)
}
}
}
}
}
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
@AllPreviews @AllPreviews
@@ -259,6 +384,7 @@ fun PreviewStatisticScreen() {
Scaffold { Scaffold {
StatisticsScreen( StatisticsScreen(
summaryPerCategoryList, summaryPerCategoryList,
summaryPerDayList,
summaryAmount = 125.24, summaryAmount = 125.24,
Currencies.entries.random(), Currencies.entries.random(),
432.14 432.14
@@ -275,6 +401,7 @@ fun PreviewStatisticScreenWithNoData() {
TripMoneyTheme { TripMoneyTheme {
Scaffold { Scaffold {
StatisticsScreen( StatisticsScreen(
emptyList(),
emptyList(), emptyList(),
summaryAmount = 0.0, summaryAmount = 0.0,
Currencies.entries.random(), Currencies.entries.random(),
@@ -285,7 +412,6 @@ fun PreviewStatisticScreenWithNoData() {
} }
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()),
@@ -303,4 +429,26 @@ val summaryPerCategoryList = listOf(
SummaryPerCategory(categories[2], 80.0, 0.2f, Currencies.PLN), SummaryPerCategory(categories[2], 80.0, 0.2f, Currencies.PLN),
SummaryPerCategory(categories[3], 50.0, 0.1f, Currencies.PLN), SummaryPerCategory(categories[3], 50.0, 0.1f, Currencies.PLN),
SummaryPerCategory(categories[5], 50.0, 0.0001f, Currencies.PLN), SummaryPerCategory(categories[5], 50.0, 0.0001f, Currencies.PLN),
) )
@RequiresApi(Build.VERSION_CODES.O)
val summaryPerDayListRaw = listOf(
SummaryPerDay(LocalDate.now(), 50.0, 0f),
SummaryPerDay(LocalDate.now().minusDays(1), 500.23, 0f),
SummaryPerDay(LocalDate.now().minusDays(2), 1560.53, 0f),
SummaryPerDay(LocalDate.now().minusDays(3), 700.32, 0f),
SummaryPerDay(LocalDate.now().minusDays(4), 201.3, 0f),
SummaryPerDay(LocalDate.now().minusDays(5), 2020.64, 0f),
SummaryPerDay(LocalDate.now().minusDays(6), 510.43, 0f),
SummaryPerDay(LocalDate.now().minusDays(7), 3050.12, 0f),
SummaryPerDay(LocalDate.now().minusDays(8), 264.32, 0f),
SummaryPerDay(LocalDate.now().minusDays(9), 3596.64, 0f)
)
@RequiresApi(Build.VERSION_CODES.O)
val highestAmount = summaryPerDayListRaw.maxOf { it.amount }
@RequiresApi(Build.VERSION_CODES.O)
val summaryPerDayList = summaryPerDayListRaw.map {
it.copy(percent = ((it.amount / highestAmount)).toFloat())
}.sortedBy { it.day.toEpochDay() }

View File

@@ -113,7 +113,7 @@ fun TripPickerScreen(
} }
}) { paddingValues -> }) { paddingValues ->
if (trips.itemCount == 0) { if (trips.itemCount == 0) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxSize().padding(10.dp), contentAlignment = Alignment.Center) {
Text( Text(
text = stringResource(string.no_trip_added), text = stringResource(string.no_trip_added),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,

View File

@@ -10,6 +10,7 @@ import androidx.paging.insertSeparators
import androidx.paging.map import androidx.paging.map
import cc.n0th1ng.tripmoney.Filter import cc.n0th1ng.tripmoney.Filter
import cc.n0th1ng.tripmoney.data.dto.SummaryPerCategory import cc.n0th1ng.tripmoney.data.dto.SummaryPerCategory
import cc.n0th1ng.tripmoney.data.dto.SummaryPerDay
import cc.n0th1ng.tripmoney.data.entity.Category import cc.n0th1ng.tripmoney.data.entity.Category
import cc.n0th1ng.tripmoney.data.entity.Expense import cc.n0th1ng.tripmoney.data.entity.Expense
import cc.n0th1ng.tripmoney.data.entity.ExpenseDto import cc.n0th1ng.tripmoney.data.entity.ExpenseDto
@@ -44,7 +45,11 @@ open class ExpenseAndCategoryViewModel @Inject constructor(
return expenseRepo.getBudgetLeft(tripId) return expenseRepo.getBudgetLeft(tripId)
} }
fun getExpensesDtoPaged(tripId: Int, search: String = "", filter: Filter = Filter()): Flow<PagingData<ExpenseDto>> = fun getExpensesDtoPaged(
tripId: Int,
search: String = "",
filter: Filter = Filter()
): Flow<PagingData<ExpenseDto>> =
expenseRepo.getExpensesDtoPaged(tripId, search, filter).cachedIn(viewModelScope) expenseRepo.getExpensesDtoPaged(tripId, search, filter).cachedIn(viewModelScope)
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
@@ -86,7 +91,11 @@ open class ExpenseAndCategoryViewModel @Inject constructor(
}.cachedIn(viewModelScope) }.cachedIn(viewModelScope)
} }
fun getExpensesDto(tripId: Int, search: String = "", filter: Filter = Filter()): Flow<List<ExpenseDto>> = fun getExpensesDto(
tripId: Int,
search: String = "",
filter: Filter = Filter()
): Flow<List<ExpenseDto>> =
expenseRepo.getExpensesDto(tripId, search, filter) expenseRepo.getExpensesDto(tripId, search, filter)
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
@@ -184,6 +193,30 @@ open class ExpenseAndCategoryViewModel @Inject constructor(
} }
} }
@RequiresApi(Build.VERSION_CODES.O)
fun getSummaryPerDay(tripId: Int): Flow<List<SummaryPerDay>> {
val tripFlow = tripRepo.getTrip(tripId)
val expensesFlow = getExpensesDto(tripId)
return tripFlow.combine(expensesFlow) { trip, expenses ->
val summaryPerDayRaw = expenses.groupBy { it.expense.datetime.toLocalDate() }
.map { (day, expensesForDay) ->
val total = expensesForDay.sumOf { it.expense.convertedAmount() }
SummaryPerDay(
amount = total,
day = day,
percent = 0.0f
)
}
.sortedByDescending { it.day }
val highestAmount = summaryPerDayRaw.maxOf { it.amount }
summaryPerDayRaw.map {
it.copy(percent = ((it.amount / highestAmount)).toFloat())
}
}
}
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
fun clearOldRates() { fun clearOldRates() {
viewModelScope.launch { viewModelScope.launch {

View File

@@ -32,4 +32,17 @@
<string name="export_csv_subttext">Zapisz wydatki z %s do pliku</string> <string name="export_csv_subttext">Zapisz wydatki z %s do pliku</string>
<string name="add_new_category">Dodaj kategorie</string> <string name="add_new_category">Dodaj kategorie</string>
<string name="edit_category">Edytuj kategorie</string> <string name="edit_category">Edytuj kategorie</string>
<string name="archive">Archiwizuj</string>
<string name="you_want_archive">Chcesz zarchiwizować?</string>
<string name="archive_category_info">Żadne wydatki nie będą usunięte.</string>
<string name="delete_category_info">Wszystkie wydatki z kategorii %s zostaną usunięte.</string>
<string name="budget">Budżet</string>
<string name="money_left">Pozostałe środki</string>
<string name="add_expense_settings">Pokaż dodawanie wydatku na starcie</string>
<string name="yesterday">Wczoraj</string>
<string name="clear">Wyczyść</string>
<string name="no_expenses">Zacznij budżetowanie od dodania wydatków</string>
<string name="no_trip_picked">Wybierz wycieczkę żeby zobaczyć wydatki</string>
<string name="no_trip_added">Zacznij budżetowanie od dodania wycieczki</string>
<string name="no_expenses_summary">Brak wydatków do podsumowania</string>
</resources> </resources>

View File

@@ -35,7 +35,7 @@
<string name="archive">Archive</string> <string name="archive">Archive</string>
<string name="you_want_archive">Do you want to archive?</string> <string name="you_want_archive">Do you want to archive?</string>
<string name="archive_category_info">No expense will be deleted.</string> <string name="archive_category_info">No expense will be deleted.</string>
<string name="delete_category_info">Your all expenses with category Hotel will be removed.</string> <string name="delete_category_info">Your all expenses with category %s will be removed.</string>
<string name="budget">Budget</string> <string name="budget">Budget</string>
<string name="money_left">Money left</string> <string name="money_left">Money left</string>
<string name="add_expense_settings">Open add expense form on startup</string> <string name="add_expense_settings">Open add expense form on startup</string>