Skip to content

Commit

Permalink
Merge pull request #13069 from woocommerce/13063-woo-poscash-receipts…
Browse files Browse the repository at this point in the history
…-implement-order-complition-call-from-the-mocked-cash-payment-screen

[Woo POS][Cash & Receipts] Navigation via navigation component to the cash payment screen
  • Loading branch information
kidinov authored Dec 11, 2024
2 parents 7ad231f + a2b4711 commit c891053
Show file tree
Hide file tree
Showing 18 changed files with 445 additions and 216 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.woocommerce.android.ui.woopos.cashpayment

import androidx.compose.animation.core.spring
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavType
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.woocommerce.android.ui.woopos.root.navigation.WooPosNavigationEvent

private const val ROUTE = "cash_payment/{orderId}"

fun NavController.navigateToCashPaymentScreen(orderId: Long) {
navigate("cash_payment/$orderId")
}

fun NavGraphBuilder.cashPaymentScreen(
onNavigationEvent: (WooPosNavigationEvent) -> Unit
) {
composable(
route = ROUTE,
arguments = listOf(
navArgument("orderId") { type = NavType.LongType }
),
enterTransition = {
slideInHorizontally(
initialOffsetX = { fullWidth -> fullWidth },
animationSpec = spring(
dampingRatio = 0.8f,
stiffness = 200f
)
)
},
exitTransition = {
slideOutHorizontally(
targetOffsetX = { fullWidth -> fullWidth },
animationSpec = spring(
dampingRatio = 0.8f,
stiffness = 200f
)
)
},
) { backStackEntry ->
WooPosCashPaymentScreen(
onNavigationEvent = onNavigationEvent,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.woocommerce.android.ui.woopos.cashpayment

import com.woocommerce.android.model.OrderMapper
import com.woocommerce.android.tools.SelectedSite
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.wordpress.android.fluxc.store.WCOrderStore
import javax.inject.Inject

class WooPosCashPaymentRepository @Inject constructor(
private val selectedSite: SelectedSite,
private val orderStore: WCOrderStore,
private val orderMapper: OrderMapper,
) {
suspend fun getOrderById(orderId: Long) = withContext(Dispatchers.IO) {
orderStore.getOrderByIdAndSite(orderId, selectedSite.get())?.let {
orderMapper.toAppModel(it)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package com.woocommerce.android.ui.woopos.cashpayment

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
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.width
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.hilt.navigation.compose.hiltViewModel
import com.woocommerce.android.R
import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview
import com.woocommerce.android.ui.woopos.common.composeui.WooPosTheme
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosButton
import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding
import com.woocommerce.android.ui.woopos.root.navigation.WooPosNavigationEvent

@Composable
fun WooPosCashPaymentScreen(onNavigationEvent: (WooPosNavigationEvent) -> Unit) {
val viewModel = hiltViewModel<WooPosCashPaymentViewModel>()
val state = viewModel.state.collectAsState().value

WooPosCashPaymentScreen(
state = state,
onAmountChanged = { viewModel.onUIEvent(WooPosCashPaymentUIEvent.AmountChanged(it)) },
onCompleteOrderClicked = { viewModel.onUIEvent(WooPosCashPaymentUIEvent.CompleteOrderClicked) },
onBackClicked = { onNavigationEvent(WooPosNavigationEvent.BackFromCashPayment) },
)
}

@Composable
fun WooPosCashPaymentScreen(
state: WooPosCashPaymentState,
onAmountChanged: (String) -> Unit,
onCompleteOrderClicked: () -> Unit,
onBackClicked: () -> Unit,
) {
Column(
modifier = Modifier
.fillMaxSize()
) {
Toolbar(onBackClicked)

when (state) {
is WooPosCashPaymentState.Collecting -> {
Collecting(
state = state,
onAmountChanged = onAmountChanged,
onCompleteOrderClicked = onCompleteOrderClicked,
)
}

WooPosCashPaymentState.Finishing -> TODO()
WooPosCashPaymentState.Initiating -> {
// show nothing
}
}
}
}

@Composable
private fun Collecting(
state: WooPosCashPaymentState.Collecting,
onAmountChanged: (String) -> Unit,
onCompleteOrderClicked: () -> Unit,
) {
Box(
modifier = Modifier.fillMaxSize()
) {
Column(
modifier = Modifier
.width(540.dp)
) {
Spacer(modifier = Modifier.weight(1f))

Text(
text = "Cash payment",
style = MaterialTheme.typography.h2,
)

Spacer(modifier = Modifier.height(16.dp))

Text(
text = "Total",
style = MaterialTheme.typography.h5,
)

Spacer(modifier = Modifier.height(8.dp))

Text(
text = state.total,
style = MaterialTheme.typography.h6,
)

Spacer(modifier = Modifier.height(16.dp))

Text(
text = "Change due",
style = MaterialTheme.typography.h5,
)

Spacer(modifier = Modifier.height(8.dp))

Text(
text = state.changeDue,
style = MaterialTheme.typography.h5,
)

Spacer(modifier = Modifier.height(16.dp))

TextField(
value = state.enteredAmount,
onValueChange = onAmountChanged,
label = { Text("Given amount") },
)

Spacer(modifier = Modifier.height(16.dp))

WooPosButton(
text = "Mark completed",
onClick = onCompleteOrderClicked,
enabled = state.canBeOrderBeCompleted,
)

Spacer(modifier = Modifier.weight(1f))
}
}
}

@Composable
private fun Toolbar(onBackClicked: () -> Unit) {
ConstraintLayout(
modifier = Modifier
.fillMaxWidth()
.padding(top = 40.dp.toAdaptivePadding())
.height(40.dp)
) {
val (backButton, title) = createRefs()
IconButton(
onClick = { onBackClicked() },
modifier = Modifier
.constrainAs(backButton) {
start.linkTo(parent.start)
top.linkTo(parent.top)
centerVerticallyTo(parent)
}
.padding(start = 8.dp.toAdaptivePadding())
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_back_24dp),
contentDescription = stringResource(R.string.woopos_cart_back_content_description),
tint = MaterialTheme.colors.onBackground,
modifier = Modifier.size(28.dp)
)
}

val iconTitlePadding = 8.dp.toAdaptivePadding()
Text(
text = stringResource(R.string.woopos_cash_payment_title),
style = MaterialTheme.typography.h4,
color = MaterialTheme.colors.onBackground,
fontWeight = FontWeight.Bold,
maxLines = 1,
modifier = Modifier
.constrainAs(title) {
top.linkTo(backButton.top)
start.linkTo(backButton.end, margin = iconTitlePadding)
centerVerticallyTo(parent)
}
)
}
}

@WooPosPreview
@Composable
fun WooPosTotalsPaymentCashScreenScreen() {
WooPosTheme {
WooPosCashPaymentScreen(
state = WooPosCashPaymentState.Collecting(
enteredAmount = "5$",
changeDue = "5$",
total = "10$",
canBeOrderBeCompleted = true,
),
onAmountChanged = {},
onCompleteOrderClicked = {},
onBackClicked = {},
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.woocommerce.android.ui.woopos.cashpayment

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
sealed class WooPosCashPaymentState : Parcelable {
data class Collecting(
val enteredAmount: String,
val changeDue: String,
val total: String,
val canBeOrderBeCompleted: Boolean,
) : WooPosCashPaymentState()

object Finishing : WooPosCashPaymentState()

object Initiating : WooPosCashPaymentState()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.woocommerce.android.ui.woopos.cashpayment

sealed class WooPosCashPaymentUIEvent {
object CompleteOrderClicked : WooPosCashPaymentUIEvent()
data class AmountChanged(val newAmount: String) : WooPosCashPaymentUIEvent()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.woocommerce.android.ui.woopos.cashpayment

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.woocommerce.android.ui.woopos.util.format.WooPosFormatPrice
import com.woocommerce.android.viewmodel.getStateFlow
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import java.math.BigDecimal
import javax.inject.Inject

@HiltViewModel
class WooPosCashPaymentViewModel @Inject constructor(
private val repository: WooPosCashPaymentRepository,
private val priceFormat: WooPosFormatPrice,
savedState: SavedStateHandle,
) : ViewModel() {
private val orderId = savedState.get<Long>("orderId")!!

private val _state = savedState.getStateFlow<WooPosCashPaymentState>(
scope = viewModelScope,
initialValue = WooPosCashPaymentState.Initiating,
key = "woo_pos_cash_payment_state"
)

val state: StateFlow<WooPosCashPaymentState> = _state

init {
viewModelScope.launch {
val order = repository.getOrderById(orderId)!!
_state.value = WooPosCashPaymentState.Collecting(
enteredAmount = "",
changeDue = priceFormat(BigDecimal.ZERO),
total = priceFormat(order.total),
canBeOrderBeCompleted = false
)
}
}

fun onUIEvent(event: WooPosCashPaymentUIEvent) {
when (event) {
is WooPosCashPaymentUIEvent.AmountChanged -> TODO()
WooPosCashPaymentUIEvent.CompleteOrderClicked -> TODO()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.woocommerce.android.ui.woopos.home

import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.slideOutHorizontally
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
Expand All @@ -27,7 +29,16 @@ fun NavGraphBuilder.homeScreen(
easing = FastOutSlowInEasing
)
)
}
},
exitTransition = {
slideOutHorizontally(
targetOffsetX = { fullWidth -> -fullWidth },
animationSpec = spring(
dampingRatio = 0.8f,
stiffness = 200f
)
)
},
) {
WooPosHomeScreen(onNavigationEvent)
}
Expand Down
Loading

0 comments on commit c891053

Please sign in to comment.