diff --git a/app/src/main/kotlin/br/com/stonks/ui/BottomAppBarLayout.kt b/app/src/main/kotlin/br/com/stonks/ui/BottomAppBarLayout.kt
index ae9c3e6..dfe5258 100644
--- a/app/src/main/kotlin/br/com/stonks/ui/BottomAppBarLayout.kt
+++ b/app/src/main/kotlin/br/com/stonks/ui/BottomAppBarLayout.kt
@@ -70,7 +70,7 @@ internal fun BottomAppBarLayout(
navController.navigate(MainNavDestination.HOME.route)
}
NavigationItem(
- icon = R.drawable.ic_alert,
+ icon = R.drawable.ic_radar,
label = br.com.stonks.R.string.main_nav_action_stock_alert,
selected = false,
) {
diff --git a/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/states/StockUiEvent.kt b/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/states/StockUiEvent.kt
index 95d5e47..0d8eb55 100644
--- a/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/states/StockUiEvent.kt
+++ b/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/states/StockUiEvent.kt
@@ -1,8 +1,15 @@
package br.com.stonks.feature.stocks.ui.states
import br.com.stonks.common.states.UiEvent
+import br.com.stonks.feature.stocks.ui.model.AlertUiModel
internal sealed class StockUiEvent : UiEvent {
- data object RegisterAlert : StockUiEvent()
+ data class RegisterAlert(
+ val data: AlertUiModel,
+ ) : StockUiEvent()
+
+ data class RemoveAlert(
+ val id: Long,
+ ) : StockUiEvent()
}
diff --git a/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/view/AlertLayout.kt b/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/view/AlertLayout.kt
new file mode 100644
index 0000000..61663fb
--- /dev/null
+++ b/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/view/AlertLayout.kt
@@ -0,0 +1,272 @@
+package br.com.stonks.feature.stocks.ui.view
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.OutlinedCard
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import br.com.stonks.common.formatters.formatCurrency
+import br.com.stonks.designsystem.components.TagLayout
+import br.com.stonks.designsystem.tokens.ColorToken
+import br.com.stonks.designsystem.tokens.SpacingToken
+import br.com.stonks.feature.stocks.R
+import br.com.stonks.feature.stocks.domain.types.StockAlertType
+import br.com.stonks.feature.stocks.domain.types.StockStatusType
+import br.com.stonks.feature.stocks.ui.model.AlertUiModel
+import br.com.stonks.feature.stocks.utils.getDescription
+import kotlinx.coroutines.launch
+
+@Composable
+private fun AlertForms(
+ uiModel: AlertUiModel,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier.fillMaxWidth()
+ .padding(SpacingToken.xl)
+ ) {
+ OutlinedTextField(
+ value = uiModel.ticket,
+ onValueChange = { },
+ enabled = false,
+ label = { Text("Ticket") },
+ modifier = Modifier.fillMaxWidth(),
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
+ )
+
+ Spacer(modifier = Modifier.height(SpacingToken.lg))
+
+ OutlinedTextField(
+ value = uiModel.alertValue.formatCurrency(),
+ onValueChange = { },
+ enabled = false,
+ label = { Text("Alertar em") },
+ modifier = Modifier.fillMaxWidth(),
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal)
+ )
+ }
+}
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+private fun AlertActions(
+ uiModel: AlertUiModel,
+ onEditItem: (data: AlertUiModel) -> Unit,
+ onDeleteItem: (id: Long) -> Unit,
+) {
+ val sheetState = rememberModalBottomSheetState()
+ val scope = rememberCoroutineScope()
+ var showBottomSheet by remember { mutableStateOf(false) }
+
+ IconButton(
+ onClick = { onDeleteItem(uiModel.id) },
+ ) {
+ Icon(
+ painter = painterResource(
+ id = br.com.stonks.designsystem.R.drawable.ic_trash
+ ),
+ contentDescription = stringResource(id = R.string.alert_action_delete),
+ )
+ }
+ IconButton(
+ onClick = { showBottomSheet = true },
+ ) {
+ Icon(
+ painter = painterResource(
+ id = br.com.stonks.designsystem.R.drawable.ic_edit
+ ),
+ contentDescription = stringResource(id = R.string.alert_action_edit),
+ )
+ }
+
+ if (showBottomSheet) {
+ ModalBottomSheet(
+ modifier = Modifier,
+ sheetState = sheetState,
+ onDismissRequest = { showBottomSheet = false },
+ ) {
+ AlertForms(
+ uiModel = uiModel,
+ )
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(SpacingToken.xl),
+ horizontalArrangement = Arrangement.End,
+ ) {
+ OutlinedButton(onClick = {
+ scope.launch { sheetState.hide() }.invokeOnCompletion {
+ if (!sheetState.isVisible) {
+ onEditItem(uiModel)
+ showBottomSheet = false
+ }
+ }
+ }) {
+ Text(stringResource(id = R.string.alert_action_close))
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun AlertLayoutTitle(
+ uiModel: AlertUiModel,
+ modifier: Modifier = Modifier,
+ onEditItem: (data: AlertUiModel) -> Unit,
+ onDeleteItem: (id: Long) -> Unit,
+) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .wrapContentHeight(),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Row(
+ modifier = Modifier.weight(1f),
+ ) {
+ Text(
+ text = uiModel.ticket,
+ style = MaterialTheme.typography.titleSmall.copy(
+ fontWeight = FontWeight.ExtraBold,
+ ),
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ TagLayout(
+ modifier = Modifier.padding(start = SpacingToken.md),
+ containerColor = uiModel.tagColor,
+ contentColor = ColorToken.NeutralWhite,
+ label = stringResource(id = uiModel.status.getDescription()),
+ )
+ }
+ AlertActions(
+ uiModel = uiModel,
+ onEditItem = { onEditItem(uiModel) },
+ onDeleteItem = { onDeleteItem(uiModel.id) },
+ )
+ }
+}
+
+@Composable
+private fun AlertLayoutContent(
+ alertValue: Double,
+ modifier: Modifier = Modifier,
+) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .wrapContentHeight()
+ ) {
+ Image(
+ painter = painterResource(id = br.com.stonks.designsystem.R.drawable.ic_broken_image),
+ contentDescription = null,
+ modifier = Modifier.size(40.dp),
+ contentScale = ContentScale.FillWidth
+ )
+ Column(
+ modifier = Modifier.padding(start = SpacingToken.lg),
+ ) {
+ Text(
+ text = stringResource(id = R.string.alert_price),
+ style = MaterialTheme.typography.titleSmall,
+ color = ColorToken.Grayscale300,
+ )
+ Spacer(modifier = Modifier.height(SpacingToken.xs))
+ Text(
+ text = alertValue.formatCurrency(),
+ style = MaterialTheme.typography.titleSmall.copy(
+ fontWeight = FontWeight.ExtraBold,
+ ),
+ )
+ }
+ }
+}
+
+@Composable
+internal fun AlertCard(
+ uiModel: AlertUiModel,
+ modifier: Modifier = Modifier,
+ onEditItem: (data: AlertUiModel) -> Unit,
+ onDeleteItem: (id: Long) -> Unit,
+) {
+ OutlinedCard(
+ modifier = modifier
+ .fillMaxWidth()
+ .wrapContentHeight(),
+ ) {
+ Column(
+ modifier = Modifier
+ .background(ColorToken.NeutralWhite)
+ .padding(SpacingToken.xl)
+ ) {
+ AlertLayoutTitle(
+ uiModel = uiModel,
+ onEditItem = { onEditItem(uiModel) },
+ onDeleteItem = { onDeleteItem(uiModel.id) },
+ )
+ HorizontalDivider(
+ modifier = Modifier.padding(vertical = SpacingToken.lg),
+ color = ColorToken.Grayscale100,
+ )
+ AlertLayoutContent(
+ alertValue = uiModel.alertValue,
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun AlertLayoutPreview() {
+ Column(
+ modifier = Modifier.padding(SpacingToken.xl)
+ ) {
+ AlertCard(
+ uiModel = AlertUiModel(
+ id = 1L,
+ ticket = "GOGL34",
+ alertValue = 71.0,
+ status = StockStatusType.AVAILABLE,
+ alert = StockAlertType.HIGH_PRICE,
+ tagColor = ColorToken.HighlightGreen,
+ ),
+ onEditItem = { },
+ onDeleteItem = { },
+ )
+ }
+}
diff --git a/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/view/StockAlertScreen.kt b/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/view/StockAlertScreen.kt
index c60174a..84f3d50 100644
--- a/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/view/StockAlertScreen.kt
+++ b/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/view/StockAlertScreen.kt
@@ -6,10 +6,6 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.SnackbarHostState
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 androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@@ -25,8 +21,10 @@ import br.com.stonks.feature.stocks.domain.types.StockAlertType
import br.com.stonks.feature.stocks.domain.types.StockStatusType
import br.com.stonks.feature.stocks.ui.model.AlertUiModel
import br.com.stonks.feature.stocks.ui.model.StockAlertUiModel
+import br.com.stonks.feature.stocks.ui.states.StockUiEvent
import br.com.stonks.feature.stocks.ui.states.StockUiState
import br.com.stonks.feature.stocks.ui.viewmodel.STOCK_VM_QUALIFIER
+import br.com.stonks.feature.stocks.ui.viewmodel.StockViewModel
import org.koin.androidx.compose.koinViewModel
import org.koin.core.qualifier.named
import timber.log.Timber
@@ -35,8 +33,8 @@ import timber.log.Timber
private fun StockAlertContent(
uiModel: StockAlertUiModel,
modifier: Modifier = Modifier,
- onEditItem: () -> Unit,
- onDeleteItem: () -> Unit,
+ onEditItem: (data: AlertUiModel) -> Unit,
+ onDeleteItem: (id: Long) -> Unit,
) {
LazyColumn(
modifier = modifier,
@@ -54,8 +52,8 @@ private fun StockAlertContent(
items(uiModel.stockAlerts) { alert ->
AlertCard(
uiModel = alert,
- onEditItem = { onEditItem() },
- onDeleteItem = { onDeleteItem() },
+ onEditItem = { onEditItem(alert) },
+ onDeleteItem = { onDeleteItem(alert.id) },
)
}
}
@@ -70,7 +68,6 @@ fun StockAlertScreen(
),
) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
- var selected by remember { mutableStateOf(false) }
when (uiState.value) {
is StockUiState.Loading -> {
@@ -81,8 +78,16 @@ fun StockAlertScreen(
StockAlertContent(
uiModel = (uiState.value as StockUiState.Success).data,
modifier = modifier,
- onEditItem = { },
- onDeleteItem = { },
+ onEditItem = { data ->
+ (viewModel as StockViewModel).dispatchUiEvent(
+ StockUiEvent.RegisterAlert(data)
+ )
+ },
+ onDeleteItem = { id ->
+ (viewModel as StockViewModel).dispatchUiEvent(
+ StockUiEvent.RemoveAlert(id)
+ )
+ },
)
}
diff --git a/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/viewmodel/StockViewModel.kt b/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/viewmodel/StockViewModel.kt
index dcccda1..a1de821 100644
--- a/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/viewmodel/StockViewModel.kt
+++ b/feature/stocks/src/main/kotlin/br/com/stonks/feature/stocks/ui/viewmodel/StockViewModel.kt
@@ -4,6 +4,7 @@ import androidx.lifecycle.viewModelScope
import br.com.stonks.common.states.ViewModelState
import br.com.stonks.feature.stocks.domain.usecase.StockAlertUseCase
import br.com.stonks.feature.stocks.ui.mapper.StockAlertUiMapper
+import br.com.stonks.feature.stocks.ui.model.AlertUiModel
import br.com.stonks.feature.stocks.ui.states.StockUiEvent
import br.com.stonks.feature.stocks.ui.states.StockUiState
import kotlinx.coroutines.flow.MutableStateFlow
@@ -28,8 +29,8 @@ internal class StockViewModel(
override fun dispatchUiEvent(uiEvent: StockUiEvent) {
when (uiEvent) {
- StockUiEvent.RegisterAlert -> TODO()
- else -> TODO()
+ is StockUiEvent.RegisterAlert -> registerAlert(uiEvent.data)
+ is StockUiEvent.RemoveAlert -> removeAlert(uiEvent.id)
}
}
@@ -45,4 +46,12 @@ internal class StockViewModel(
}
}
}
+
+ private fun registerAlert(alert: AlertUiModel) {
+ Timber.e("Register or edit stock alert: $alert")
+ }
+
+ private fun removeAlert(id: Long) {
+ Timber.e("Remove stock alert ID: $id")
+ }
}
diff --git a/feature/stocks/src/main/res/values/strings.xml b/feature/stocks/src/main/res/values/strings.xml
index 932344f..f106667 100644
--- a/feature/stocks/src/main/res/values/strings.xml
+++ b/feature/stocks/src/main/res/values/strings.xml
@@ -7,6 +7,7 @@
Valor do produto
Excluir alerta
Editar alerta
+ Fechar
Salvar
Cancelar