From c79c0bba0d5c7a7867df11c3851d0d154e2bea15 Mon Sep 17 00:00:00 2001 From: Iliyan Germanov Date: Sat, 15 Apr 2023 21:37:21 +0300 Subject: [PATCH] WIP: Manual CSV mapping --- .../java/com/ivy/wallet/ui/csv/CSVEvent.kt | 8 + .../java/com/ivy/wallet/ui/csv/CSVScreen.kt | 196 ++++++++++++++++-- .../java/com/ivy/wallet/ui/csv/CSVState.kt | 19 +- 3 files changed, 196 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/ivy/wallet/ui/csv/CSVEvent.kt b/app/src/main/java/com/ivy/wallet/ui/csv/CSVEvent.kt index 36ff3201a6..76e929653c 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csv/CSVEvent.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csv/CSVEvent.kt @@ -5,4 +5,12 @@ import android.net.Uri sealed interface CSVEvent { data class FilePicked(val uri: Uri) : CSVEvent + data class MapAmount(val index: Int, val name: String) : CSVEvent + data class AmountMultiplier(val multiplier: Int) : CSVEvent + data class MapType(val index: Int, val name: String) : CSVEvent + data class TypeMetaChange(val meta: TrnTypeMetadata) : CSVEvent + data class MapDate(val index: Int, val name: String) : CSVEvent + data class DataMetaChange(val meta: DateMetadata) : CSVEvent + data class MapAccount(val index: Int, val name: String) : CSVEvent + data class MapAccountCurrency(val index: Int, val name: String) : CSVEvent } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/ui/csv/CSVScreen.kt b/app/src/main/java/com/ivy/wallet/ui/csv/CSVScreen.kt index e38ec9ab2c..ca1a5e8c0f 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csv/CSVScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csv/CSVScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.Text +import androidx.compose.material.TextField import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -83,10 +84,15 @@ fun LazyListScope.spacer8() { } @Composable -fun Spacer8() { - Spacer(modifier = Modifier.height(8.dp)) +fun Spacer8(horizontal: Boolean = false) { + if (horizontal) { + Spacer(modifier = Modifier.width(8.dp)) + } else { + Spacer(modifier = Modifier.height(8.dp)) + } } + private fun LazyListScope.csvTable( csv: List ) { @@ -165,17 +171,11 @@ private fun LazyListScope.mappingRow( verticalAlignment = Alignment.CenterVertically ) { columns.values.forEachIndexed { index, column -> - Button( - colors = if (column == mapping.name) { - ButtonDefaults.buttonColors() - } else { - ButtonDefaults.outlinedButtonColors() - }, - onClick = { - onMapTo(index, column) - } + EnabledButton( + text = column, + enabled = column == mapping.name ) { - Text(text = column) + onMapTo(index, column) } Spacer(Modifier.width(8.dp)) } @@ -191,7 +191,6 @@ private fun LazyListScope.mappingRow( CSVRow(row = CSVRow(mapping.sampleValues), header = false, even = true) } } - Spacer8() } } @@ -203,6 +202,7 @@ fun LazyListScope.sectionDivider(text: String) { } } +// region Important fun LazyListScope.important( columns: CSVRow, importantFields: ImportantFields, @@ -212,11 +212,173 @@ fun LazyListScope.important( mappingRow( columns = columns, mapping = importantFields.amount, - onMapTo = { index, name -> - - }, + onMapTo = { index, name -> onEvent(CSVEvent.MapAmount(index, name)) }, + metadataContent = { multiplier -> + AmountMetadata(multiplier = multiplier, onEvent = onEvent) + } + ) + mappingRow( + columns = columns, + mapping = importantFields.type, + onMapTo = { index, name -> onEvent(CSVEvent.MapType(index, name)) }, metadataContent = { + TypeMetadata(metadata = it, onEvent = onEvent) + } + ) + mappingRow( + columns = columns, + mapping = importantFields.date, + onMapTo = { index, name -> onEvent(CSVEvent.MapDate(index, name)) }, + metadataContent = { + DateMetadataUI(metadata = it, onEvent = onEvent) + } + ) + mappingRow( + columns = columns, + mapping = importantFields.account, + onMapTo = { index, name -> onEvent(CSVEvent.MapAccount(index, name)) }, + ) + mappingRow( + columns = columns, + mapping = importantFields.accountCurrency, + onMapTo = { index, name -> onEvent(CSVEvent.MapAccountCurrency(index, name)) }, + ) +} +@Composable +private fun AmountMetadata( + multiplier: Int, + onEvent: (CSVEvent) -> Unit, +) { + Row(verticalAlignment = Alignment.CenterVertically) { + Button(onClick = { + onEvent( + CSVEvent.AmountMultiplier( + when { + multiplier < 0 -> multiplier * 10 + multiplier == 1 -> -10 + else -> multiplier / 10 + } + ) + ) + }) { + Text(text = "/10") + } + Spacer8(horizontal = true) + Text( + text = when { + multiplier < 0 -> "/$multiplier" + multiplier > 1 -> "*$multiplier" + else -> "None" + } + ) + Spacer8(horizontal = true) + Button(onClick = { + onEvent( + CSVEvent.AmountMultiplier( + when { + multiplier == -1 -> 10 + multiplier > 0 -> multiplier * 10 + else -> multiplier / 10 + } + ) + ) + }) { + Text(text = "*10") + } + } +} + +// region Type Metadata +@Composable +private fun TypeMetadata( + metadata: TrnTypeMetadata, + onEvent: (CSVEvent) -> Unit +) { + val onTypeMetaEvent = { newMeta: TrnTypeMetadata -> + onEvent(CSVEvent.TypeMetaChange(newMeta)) + } + + LabelEqualsField( + label = "Income", + value = metadata.income, + onValueChange = { + onTypeMetaEvent(metadata.copy(income = it)) + } + ) + Spacer8() + LabelEqualsField( + label = "Expense", + value = metadata.expense, + onValueChange = { + onTypeMetaEvent(metadata.copy(expense = it)) } ) -} \ No newline at end of file + Spacer8() + Text(text = "(optional)", style = UI.typo.c) + LabelEqualsField( + label = "Transfer", + value = metadata.transfer ?: "", + onValueChange = { + onTypeMetaEvent(metadata.copy(transfer = it)) + } + ) +} + +@Composable +fun LabelEqualsField( + label: String, + value: String, + onValueChange: (String) -> Unit, +) { + Row(verticalAlignment = Alignment.CenterVertically) { + Text(text = label, color = UI.colors.primary) + Spacer8(horizontal = true) + TextField(value = value, onValueChange = onValueChange) + } +} +// endregion + +@Composable +private fun DateMetadataUI( + metadata: DateMetadata, + onEvent: (CSVEvent) -> Unit, +) { + Text(text = "Which is first in the format?") + Row { + EnabledButton( + enabled = metadata == DateMetadata.DateFirst, + text = "Date/Day (1,2,3...31)", + onClick = { + onEvent(CSVEvent.DataMetaChange(DateMetadata.DateFirst)) + } + ) + Spacer8(horizontal = true) + EnabledButton( + enabled = metadata == DateMetadata.MonthFirst, + text = "Month (1,2..12 / Jan, Feb..Dec)", + onClick = { + onEvent(CSVEvent.DataMetaChange(DateMetadata.MonthFirst)) + } + ) + } +} + +@Composable +private fun EnabledButton( + text: String, + enabled: Boolean, + onClick: () -> Unit, +) { + Button( + colors = if (enabled) { + ButtonDefaults.buttonColors() + } else { + ButtonDefaults.outlinedButtonColors() + }, + onClick = onClick + ) { + Text(text = text) + } +} +// endregion \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/ui/csv/CSVState.kt b/app/src/main/java/com/ivy/wallet/ui/csv/CSVState.kt index c6697f10aa..1e643140f1 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csv/CSVState.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csv/CSVState.kt @@ -1,35 +1,34 @@ package com.ivy.wallet.ui.csv -import com.ivy.wallet.domain.data.core.Transaction - data class CSVState( val columns: CSVRow, val csv: List?, val important: ImportantFields?, val transfer: TransferFields?, val optional: OptionalFields?, - val transactions: List? + + val successPercent: Double, + val failedRows: List?, ) data class ImportantFields( - val amount: ColumnMapping, + val amount: ColumnMapping, val type: ColumnMapping, - val date: ColumnMapping, + val date: ColumnMapping, val account: ColumnMapping, val accountCurrency: ColumnMapping, ) -sealed interface AmountMetadata { - data class Multiple(val multiplier: Int) : AmountMetadata - data class Divider(val divider: Int) : AmountMetadata -} - data class TrnTypeMetadata( val income: String, val expense: String, val transfer: String?, ) +enum class DateMetadata { + MonthFirst, DateFirst +} + data class TransferFields( val toAccount: ColumnMapping, val toAccountCurrency: ColumnMapping,