Skip to content

Commit

Permalink
Merge pull request #13123 from woocommerce/13117-woo-poscash-receipts…
Browse files Browse the repository at this point in the history
…-implement-receipts-ui

[Woo POS][Cash & Receipts] Implement receipts UI
  • Loading branch information
samiuelson authored Dec 17, 2024
2 parents 71e0fae + 55a9e12 commit 284cdf1
Show file tree
Hide file tree
Showing 14 changed files with 614 additions and 125 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@ package com.woocommerce.android.ui.woopos.cashpayment
import androidx.compose.foundation.layout.Column
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.text.KeyboardOptions
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
Expand All @@ -21,10 +17,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
Expand All @@ -38,6 +32,7 @@ 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.component.WooPosButtonState
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosToolbar
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosTypedInputField
import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding
import com.woocommerce.android.ui.woopos.root.navigation.WooPosNavigationEvent
Expand All @@ -53,7 +48,7 @@ fun WooPosCashPaymentScreen(onNavigationEvent: (WooPosNavigationEvent) -> Unit)
state = state,
onAmountChanged = { viewModel.onUIEvent(WooPosCashPaymentUIEvent.AmountChanged(it)) },
onCompleteOrderClicked = { viewModel.onUIEvent(WooPosCashPaymentUIEvent.CompleteOrderClicked) },
onBackClicked = { onNavigationEvent(WooPosNavigationEvent.BackFromCashPayment) },
onBackClicked = { onNavigationEvent(WooPosNavigationEvent.GoBack) },
onOrderComplete = { onNavigationEvent(WooPosNavigationEvent.OpenHomeFromCashPaymentAfterSuccessfulPayment) },
)
}
Expand All @@ -69,7 +64,10 @@ fun WooPosCashPaymentScreen(
Column(
modifier = Modifier.fillMaxSize()
) {
Toolbar(onBackClicked)
WooPosToolbar(
titleText = stringResource(R.string.woopos_cash_payment_title),
onBackClicked = onBackClicked,
)

when (state) {
is WooPosCashPaymentState.Collecting -> {
Expand Down Expand Up @@ -188,50 +186,6 @@ private fun Collecting(
}
}

@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 WooPosTotalsPaymentCashScreenPreview() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview
Expand Down Expand Up @@ -159,7 +160,7 @@ fun WooPosOutlinedButton(
border = BorderStroke(2.dp, MaterialTheme.colors.onBackground),
shape = RoundedCornerShape(8.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.background,
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.onBackground,
),
elevation = ButtonDefaults.elevation(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
package com.woocommerce.android.ui.woopos.common.composeui.component

import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextFieldColors
import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
Expand Down Expand Up @@ -92,9 +106,62 @@ fun <T> WooPosTypedInputField(
}
}

@Composable
fun WooPosInputField(
value: String,
onValueChange: (String) -> Unit,
label: String = "",
textStyle: TextStyle = MaterialTheme.typography.h6,
textColor: Color = MaterialTheme.colors.onBackground,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
contentAlignment: Alignment = Alignment.CenterStart,
modifier: Modifier = Modifier,
) {
var labelWidth by remember { mutableIntStateOf(0) }

Box(
modifier = modifier
.background(Color.Transparent),
contentAlignment = contentAlignment,
) {
if (value.isEmpty()) {
Text(
text = label,
style = textStyle.copy(color = textColor.copy(alpha = 0.2f)),
maxLines = 1,
softWrap = false,
modifier = Modifier
.onGloballyPositioned { coordinates ->
labelWidth = coordinates.size.width
}
)
}

val density = LocalDensity.current

// that's workaround to keep cursor to the left from the label
val textFieldModifier = if (value.isEmpty()) {
Modifier.width(with(density) { labelWidth.toDp() + 4.dp })
} else {
Modifier.width(IntrinsicSize.Min)
}
BasicTextField(
value = value,
onValueChange = onValueChange,
textStyle = textStyle.copy(color = textColor),
singleLine = true,
keyboardActions = keyboardActions,
keyboardOptions = keyboardOptions,
modifier = textFieldModifier,
cursorBrush = SolidColor(MaterialTheme.colors.onBackground),
)
}
}

@WooPosPreview
@Composable
fun WooPosInputFieldPreview() {
fun WooPosTypedInputFieldPreview() {
WooPosTheme {
Column(modifier = Modifier.padding(16.dp)) {
WooPosTypedInputField(
Expand Down Expand Up @@ -125,3 +192,32 @@ fun WooPosInputFieldPreview() {
}
}
}

@WooPosPreview
@Composable
fun WooPosInputFieldPreview() {
WooPosTheme {
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
WooPosInputField(
value = "[email protected]",
onValueChange = {},
textStyle = MaterialTheme.typography.h3,
contentAlignment = Alignment.Center
)

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

WooPosInputField(
value = "",
onValueChange = {},
textStyle = MaterialTheme.typography.h3,
label = "Label Label",
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.woocommerce.android.ui.woopos.common.composeui.component

import androidx.compose.foundation.layout.Column
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.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
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 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.toAdaptivePadding

@Composable
fun WooPosToolbar(
titleText: String,
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_toolbar_icon_content_description),
tint = MaterialTheme.colors.onBackground,
modifier = Modifier.size(28.dp)
)
}

val iconTitlePadding = 8.dp.toAdaptivePadding()
Text(
text = titleText,
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 WooPosToolbarPreview() {
WooPosTheme {
Column {
WooPosToolbar(
titleText = "Title",
onBackClicked = { }
)

Spacer(modifier = Modifier.weight(1f))
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.woocommerce.android.ui.woopos.emailreceipt

import android.util.Patterns
import com.woocommerce.android.model.Order
import com.woocommerce.android.model.OrderMapper
import com.woocommerce.android.tools.SelectedSite
Expand All @@ -14,6 +15,7 @@ class WooPosEmailReceiptRepository @Inject constructor(
private val orderStore: WCOrderStore,
private val orderCreateEditRepository: OrderCreateEditRepository,
private val orderMapper: OrderMapper,
private val provideEmailPattern: WooPosProvideEmailPattern,
) {
suspend fun sendReceiptByEmail(orderId: Long, email: String): Result<Unit> = withContext(Dispatchers.IO) {
val order = getOrderById(orderId)
Expand All @@ -28,6 +30,8 @@ class WooPosEmailReceiptRepository @Inject constructor(
return@withContext triggerOrderReceiptSending(orderId)
}

fun isEmailValid(email: String): Boolean = provideEmailPattern().matcher(email).matches()

private suspend fun triggerOrderReceiptSending(orderId: Long): Result<Unit> {
val sendOrderResult = orderStore.sendOrderReceipt(selectedSite.get(), orderId)
return if (sendOrderResult.isError) {
Expand All @@ -50,3 +54,7 @@ class WooPosEmailReceiptRepository @Inject constructor(
orderMapper.toAppModel(it)
}
}

class WooPosProvideEmailPattern @Inject constructor() {
operator fun invoke() = Patterns.EMAIL_ADDRESS
}
Loading

0 comments on commit 284cdf1

Please sign in to comment.