Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.woocommerce.android.ui.woopos.common.composeui.component

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

val WooPosButtonModifier = Modifier
.fillMaxWidth(0.3f)
.height(80.dp)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Arrangement
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
Expand Down Expand Up @@ -38,7 +37,8 @@ fun WooPosErrorScreen(
secondaryButton: Button? = null
) {
Column(
modifier = modifier.fillMaxSize()
modifier = modifier
.fillMaxSize()
.clip(RoundedCornerShape(WooPosCornerRadius.Medium.value))
.padding(WooPosSpacing.XLarge.value.toAdaptivePadding()),
horizontalAlignment = Alignment.CenterHorizontally,
Expand Down Expand Up @@ -77,19 +77,15 @@ fun WooPosErrorScreen(
WooPosButton(
text = it.text,
onClick = it.click,
modifier = Modifier
.fillMaxWidth(.5f)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kidinov In this case (and in the empty state), the button has a narrower width in the design. Let me know if you have a better idea for how to adapt it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That changes the state on all 11 places where this component is used. If this is what Wagner's idea was, then it's fine. Overall, I think it looks ok

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to extract that constant so that changing the width will only require one change. This is because both buttons must have the same width

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, I created a shared modifier here 3c3b5f4

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, make it more specific by not calling it just Button

.height(80.dp)
modifier = WooPosButtonModifier
)
}
secondaryButton?.let {
Spacer(modifier = Modifier.height(WooPosSpacing.Medium.value.toAdaptivePadding()))
WooPosOutlinedButton(
text = it.text,
onClick = it.click,
modifier = Modifier
.fillMaxWidth(.5f)
.height(80.dp)
modifier = WooPosButtonModifier
)
}
Spacer(modifier = Modifier.height(WooPosSpacing.Medium.value.toAdaptivePadding()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import com.woocommerce.android.R
import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview
import com.woocommerce.android.ui.woopos.common.composeui.component.ShadowType
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosButton
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosButtonModifier
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosCard
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosLazyColumn
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosShimmerBox
Expand Down Expand Up @@ -620,9 +621,7 @@ private fun WooPosItemsEmptyListInternal(
WooPosButton(
text = actionLabel,
onClick = onActionClicked,
modifier = Modifier
.fillMaxWidth(0.5f)
.height(80.dp)
modifier = WooPosButtonModifier
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,17 @@ import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.selected
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.woocommerce.android.R
import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview
import com.woocommerce.android.ui.woopos.common.composeui.component.Button
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosErrorScreen
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosPaginationErrorIndicator
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosSearchInput
import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosSearchInputState
Expand All @@ -55,6 +56,7 @@ import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosToolba
import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosSpacing
import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosTheme
import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosTypography
import com.woocommerce.android.ui.woopos.home.items.WooPosItemsEmptyList
import com.woocommerce.android.ui.woopos.home.items.WooPosPaginationState
import com.woocommerce.android.ui.woopos.home.items.WooPosPullToRefreshState
import com.woocommerce.android.ui.woopos.root.navigation.WooPosNavigationEvent
Expand All @@ -74,15 +76,45 @@ fun WooPosOrdersScreen(
val onBackClicked = { onNavigationEvent(WooPosNavigationEvent.GoBack) }
BackHandler { onNavigationEvent(WooPosNavigationEvent.GoBack) }

Box(modifier = Modifier.fillMaxSize()) {
when (val currentState = state) {
is WooPosOrdersState.Content -> OrdersContent(currentState, viewModel)
is WooPosOrdersState.Empty -> OrdersEmpty(
onActionClicked = { viewModel::onOrdersEmptyActionClicked }
)

is WooPosOrdersState.Error -> OrdersError(
onRetryClicked = viewModel::onOrdersLoadingErrorRetryButtonClicked
)

is WooPosOrdersState.Loading -> {
// full screen loading state
}
}

if (state.searchInputState is WooPosSearchInputState.Closed) {
WooPosToolbar(
titleText = stringResource(R.string.woopos_orders_title),
onBackClicked = onBackClicked,
modifier = Modifier.fillMaxWidth()
)
}
}
}

@Composable
private fun OrdersContent(
state: WooPosOrdersState.Content,
viewModel: WooPosOrdersViewModel
) {
Row(modifier = Modifier.fillMaxSize()) {
OrdersList(
OrdersListPane(
state = state,
onBackClicked = onBackClicked,
onRefresh = viewModel::onRefresh,
isRefreshing = state.pullToRefreshState == WooPosPullToRefreshState.Refreshing,
onOrderSelected = viewModel::onOrderSelected,
onEndOfOrdersListReached = viewModel::onEndOfOrdersListReached,
viewModel::onPaginationErrorTryAgain,
onPaginationErrorTryAgain = viewModel::onPaginationErrorTryAgain,
onSearchEvent = viewModel::onSearchEvent,
modifier = Modifier
.weight(0.3f)
Expand All @@ -91,20 +123,19 @@ fun WooPosOrdersScreen(
)

OrderDetails(
state = state,
modifier = Modifier
.weight(0.7f)
.fillMaxHeight()
.background(MaterialTheme.colorScheme.surfaceContainerLow)
.background(MaterialTheme.colorScheme.surfaceContainerLow),
order = state.items.find { it.id == state.selectedOrderId },
)
}
}

@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun OrdersList(
state: WooPosOrdersState,
onBackClicked: () -> Unit,
private fun OrdersListPane(
state: WooPosOrdersState.Content,
onRefresh: () -> Unit,
isRefreshing: Boolean,
onOrderSelected: (Long) -> Unit,
Expand All @@ -114,40 +145,17 @@ private fun OrdersList(
modifier: Modifier = Modifier
) {
Column(modifier = modifier) {
ConstraintLayout(
Box(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = WOO_POS_ORDERS_TOOLBAR_HEIGHT),
) {
val (toolbar, searchInput) = createRefs()

if (state.searchInputState is WooPosSearchInputState.Closed) {
WooPosToolbar(
titleText = stringResource(R.string.woopos_orders_title),
onBackClicked = onBackClicked,
modifier = Modifier.constrainAs(toolbar) {
start.linkTo(parent.start)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
}
)
}
WooPosSearchInput(
state = state.searchInputState,
onEvent = onSearchEvent,
modifier = Modifier
.statusBarsPadding()
.constrainAs(searchInput) {
if (state.searchInputState is WooPosSearchInputState.Open) {
start.linkTo(parent.start)
end.linkTo(parent.end)
width = androidx.constraintlayout.compose.Dimension.fillToConstraints
} else {
end.linkTo(parent.end)
}
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
}
.align(Alignment.CenterEnd)
)
}

Expand All @@ -166,59 +174,13 @@ private fun OrdersList(
enabled = state.pullToRefreshState != WooPosPullToRefreshState.Disabled
)
) {
when (state) {
is WooPosOrdersState.Loading -> {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
WooPosText(
text = stringResource(R.string.loading),
style = WooPosTypography.BodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(WooPosSpacing.Large.value)
)
}
}

is WooPosOrdersState.Error -> {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
WooPosText(
text = state.message,
style = WooPosTypography.BodyMedium,
color = MaterialTheme.colorScheme.error,
modifier = Modifier.padding(WooPosSpacing.Large.value)
)
}
}

is WooPosOrdersState.Empty -> {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
WooPosText(
text = "No orders found",
style = WooPosTypography.BodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(WooPosSpacing.Large.value)
)
}
}

is WooPosOrdersState.Content -> {
WooPosOrdersListPaneScreen(
modifier = Modifier.fillMaxSize(),
state = state,
onOrderSelected = onOrderSelected,
onEndOfOrdersListReached = onEndOfOrdersListReached,
onPaginationErrorTryAgain = onPaginationErrorTryAgain,
)
}
}
OrdersList(
modifier = Modifier.fillMaxSize(),
state = state,
onOrderSelected = onOrderSelected,
onEndOfOrdersListReached = onEndOfOrdersListReached,
onPaginationErrorTryAgain = onPaginationErrorTryAgain,
)

PullRefreshIndicator(
refreshing = isRefreshing,
Expand All @@ -234,23 +196,7 @@ private fun OrdersList(
}

@Composable
private fun OrderDetails(
state: WooPosOrdersState,
modifier: Modifier = Modifier
) {
val selectedItem: OrderItemViewState? = when (state) {
is WooPosOrdersState.Content -> state.items.firstOrNull { it.id == state.selectedOrderId }
else -> null
}

WooPosOrdersDetailPaneScreen(
selected = selectedItem,
modifier = modifier.fillMaxSize()
)
}

@Composable
fun WooPosOrdersListPaneScreen(
private fun OrdersList(
modifier: Modifier = Modifier,
state: WooPosOrdersState.Content,
onOrderSelected: (Long) -> Unit,
Expand Down Expand Up @@ -335,14 +281,14 @@ fun WooPosOrdersListPaneScreen(
}

@Composable
fun WooPosOrdersDetailPaneScreen(
selected: OrderItemViewState?,
modifier: Modifier = Modifier
private fun OrderDetails(
modifier: Modifier = Modifier,
order: OrderItemViewState?,
) {
Column(modifier = modifier.fillMaxSize()) {
WooPosToolbar(
modifier = Modifier.fillMaxWidth(),
titleText = selected?.title ?: "--",
titleText = order?.title ?: "--",
titleFontWeight = FontWeight.Bold
)
Column(
Expand Down Expand Up @@ -397,6 +343,35 @@ private fun OrdersPaginationLoadingRow() {
}
}

@Composable
fun OrdersEmpty(
onActionClicked: () -> Unit
) {
WooPosItemsEmptyList(
modifier = Modifier.fillMaxSize(),
icon = painterResource(id = R.drawable.ic_woo_pos_orders_empty),
title = stringResource(id = R.string.woopos_orders_empty_list_title),
message = stringResource(id = R.string.woopos_orders_empty_list_message),
contentDescription = stringResource(id = R.string.woopos_coupons_empty_list_image_description),
actionLabel = stringResource(id = R.string.woopos_orders_empty_action_label),
onActionClicked = onActionClicked
)
}

@Composable
fun OrdersError(
onRetryClicked: () -> Unit
) {
WooPosErrorScreen(
message = stringResource(id = R.string.woopos_orders_loading_error_title),
reason = stringResource(id = R.string.woopos_orders_loading_error_message),
primaryButton = Button(
text = stringResource(id = R.string.woopos_orders_loading_error_retry_button),
click = onRetryClicked
)
)
}

@Composable
private fun OrdersPaginationErrorRow(onPaginationErrorTryAgain: () -> Unit) {
WooPosPaginationErrorIndicator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,15 @@ class WooPosOrdersViewModel @Inject constructor(
loadMoreIfPossible()
}

fun onOrdersEmptyActionClicked() {
// Action to be defined
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just sharing my perspective - I think the only CTA needed on an empty state is "create a new something" one, and here it doesn’t apply. I think only what we need is to have pull to refresh enabled on this state, and that's it. There is nothing to "learn more" here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your input @kidinov! Sharing with our project lead @staskus

Copy link

@staskus staskus Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My first move was to show "Refresh" as well, but the design change was to go from "Refresh" to "Learn More". pdfdoF-7Av-p2#comment-9460

}

fun onOrdersLoadingErrorRetryButtonClicked() {
_state.value = WooPosOrdersState.Loading(searchInputState = WooPosSearchInputState.Closed)
loadOrders()
}

@Suppress("ReturnCount")
fun loadMoreIfPossible() {
if (loadingJob?.isActive == true || loadingMoreOrdersJob?.isActive == true) return
Expand Down
Loading