Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.woocommerce.android.ui.bookings.compose.BookingPaymentDetailsModel
import com.woocommerce.android.ui.bookings.compose.BookingStaffMemberStatus
import com.woocommerce.android.ui.bookings.compose.BookingStatus
import com.woocommerce.android.ui.bookings.compose.BookingSummaryModel
import com.woocommerce.android.ui.bookings.details.AttendanceUpdateStatus
import com.woocommerce.android.ui.bookings.details.CancelStatus
import com.woocommerce.android.ui.bookings.list.BookingListItem
import com.woocommerce.android.util.CurrencyFormatter
Expand Down Expand Up @@ -41,20 +42,21 @@ class BookingMapper @Inject constructor(
private val timeRangeFormatter: DateTimeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
.withZone(ZoneOffset.UTC)

fun Booking.toBookingSummaryModel(): BookingSummaryModel {
fun Booking.toBookingSummaryModel(attendanceUpdateStatus: AttendanceUpdateStatus): BookingSummaryModel {
return BookingSummaryModel(
date = summaryDateFormatter.format(start),
name = order.productInfo?.name ?: "-",
customerName = order.customerInfo?.fullName(),
attendanceStatus = BookingAttendanceStatus.BOOKED,
status = status.toUiModel()
attendanceStatus = attendanceStatus.toUiModel(),
status = status.toUiModel(),
attendanceUpdateStatus = attendanceUpdateStatus,
)
}

fun Booking.toListItem(): BookingListItem {
return BookingListItem(
id = id.value,
summary = toBookingSummaryModel()
summary = toBookingSummaryModel(AttendanceUpdateStatus.Idle)
)
}

Expand Down Expand Up @@ -110,6 +112,13 @@ class BookingMapper @Inject constructor(
is BookingEntity.Status.Unknown -> BookingStatus.Unknown(this.key)
}

private fun BookingEntity.AttendanceStatus.toUiModel(): BookingAttendanceStatus = when (this) {
BookingEntity.AttendanceStatus.Booked -> BookingAttendanceStatus.Booked
BookingEntity.AttendanceStatus.CheckedIn -> BookingAttendanceStatus.CheckedIn
BookingEntity.AttendanceStatus.NoShow -> BookingAttendanceStatus.NoShow
Comment on lines +116 to +118
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As you can see, there's no Cancelled here - not sure yet how we will determine such attendance status.

is BookingEntity.AttendanceStatus.Unknown -> BookingAttendanceStatus.Unknown(this.key)
}

private fun BookingCustomerInfo.fullName(): String? {
return "${billingFirstName.orEmpty()} ${billingLastName.orEmpty()}".trim().ifEmpty { null }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,22 @@ class BookingsRepository @Inject constructor(
}
}

suspend fun updateAttendanceStatus(
bookingId: Long,
attendanceStatus: BookingEntity.AttendanceStatus,
): Result<Unit> {
val result = bookingsStore.updateAttendanceStatus(
site = selectedSite.get(),
bookingId = bookingId,
attendanceStatus = attendanceStatus
)
return if (result.isError) {
Result.failure(WooException(result.error))
} else {
Result.success(Unit)
}
}

data class FetchResult(
val bookings: List<Booking>,
val hasMorePages: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,35 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.woocommerce.android.R
import com.woocommerce.android.ui.bookings.details.AttendanceUpdateStatus
import com.woocommerce.android.ui.compose.animations.SkeletonView
import com.woocommerce.android.ui.compose.preview.LightDarkThemePreviews
import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground

@Composable
fun BookingAttendanceSection(
status: BookingAttendanceStatus,
attendanceUpdateStatus: AttendanceUpdateStatus,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Expand All @@ -37,8 +48,11 @@ fun BookingAttendanceSection(
HorizontalDivider(thickness = 0.5.dp)
AttendanceRow(
label = R.string.booking_attendance_label_status,
attendanceUpdateStatus = attendanceUpdateStatus,
value = status.text(),
modifier = Modifier.clickable { onClick() }
modifier = Modifier.clickable {
if (attendanceUpdateStatus == AttendanceUpdateStatus.Idle) onClick()
}
)
HorizontalDivider(thickness = 0.5.dp)
}
Expand All @@ -54,9 +68,12 @@ fun BookingAttendanceSection(
@Composable
private fun AttendanceRow(
@StringRes label: Int,
attendanceUpdateStatus: AttendanceUpdateStatus,
value: String,
modifier: Modifier = Modifier,
) {
val density = LocalDensity.current
var skeletonSize by remember { mutableStateOf(DpSize.Zero) }
Column(modifier = modifier) {
Row(
verticalAlignment = Alignment.CenterVertically,
Expand All @@ -66,20 +83,39 @@ private fun AttendanceRow(
.padding(horizontal = 16.dp, vertical = 12.dp)
) {
BookingDetailsLabel(label)
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = value,
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Icon(
painter = painterResource(id = R.drawable.ic_arrow_right),
contentDescription = null,
tint = MaterialTheme.colorScheme.surfaceVariant,
modifier = Modifier.padding(start = 8.dp)
)
when (attendanceUpdateStatus) {
AttendanceUpdateStatus.InProgress -> {
SkeletonView(
modifier = Modifier
.size(skeletonSize)
)
}

AttendanceUpdateStatus.Idle -> {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.onSizeChanged {
with(density) {
skeletonSize = DpSize(it.width.toDp(), it.height.toDp())
}
}
) {
Text(
text = value,
style = MaterialTheme.typography.labelLarge,
Comment on lines +86 to +106
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

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

Skeleton uses size(skeletonSize) with no minimums; on first render in InProgress state, it will render at 0×0. Add a fallback size (e.g., sizeIn(minWidth = 80.dp, minHeight = 20.dp)) or conditionally use a default when skeletonSize == DpSize.Zero so loading is visible.

Copilot uses AI. Check for mistakes.

color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Icon(
painter = painterResource(id = R.drawable.ic_arrow_right),
contentDescription = null,
tint = MaterialTheme.colorScheme.surfaceVariant,
modifier = Modifier.padding(start = 8.dp)
)
}
}
}
}
}
Expand All @@ -90,7 +126,21 @@ private fun AttendanceRow(
private fun BookingAttendanceSectionPreview() {
WooThemeWithBackground {
BookingAttendanceSection(
status = BookingAttendanceStatus.BOOKED,
status = BookingAttendanceStatus.Booked,
attendanceUpdateStatus = AttendanceUpdateStatus.Idle,
onClick = {},
modifier = Modifier.fillMaxWidth()
)
}
}

@LightDarkThemePreviews
@Composable
private fun BookingAttendanceSectionInProgressPreview() {
WooThemeWithBackground {
BookingAttendanceSection(
status = BookingAttendanceStatus.Booked,
attendanceUpdateStatus = AttendanceUpdateStatus.InProgress,
onClick = {},
modifier = Modifier.fillMaxWidth()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ private fun BookingAttendanceStatusSelection(
modifier = Modifier.padding(bottom = 22.dp)
)
listOf(
BookingAttendanceStatus.BOOKED,
BookingAttendanceStatus.CHECKED_IN,
BookingAttendanceStatus.NO_SHOW,
BookingAttendanceStatus.Booked,
BookingAttendanceStatus.CheckedIn,
BookingAttendanceStatus.NoShow,
).forEachIndexed { index, status ->
AttendanceStatusRow(
status = status,
Expand Down Expand Up @@ -122,17 +122,17 @@ private fun AttendanceStatusRow(

@Composable
private fun BookingAttendanceStatus.description(): String = when (this) {
BookingAttendanceStatus.BOOKED -> R.string.booking_attendance_status_booked_desc
BookingAttendanceStatus.CHECKED_IN -> R.string.booking_attendance_status_checked_in_desc
BookingAttendanceStatus.NO_SHOW -> R.string.booking_attendance_status_no_show_desc
BookingAttendanceStatus.Booked -> R.string.booking_attendance_status_booked_desc
BookingAttendanceStatus.CheckedIn -> R.string.booking_attendance_status_checked_in_desc
BookingAttendanceStatus.NoShow -> R.string.booking_attendance_status_no_show_desc
else -> null
}?.let { stringResource(it) } ?: ""

private val BookingAttendanceStatus.iconRes: Int?
get() = when (this) {
BookingAttendanceStatus.BOOKED -> R.drawable.ic_attendance_booked
BookingAttendanceStatus.CHECKED_IN -> R.drawable.ic_attendance_checked_in
BookingAttendanceStatus.NO_SHOW -> R.drawable.ic_attendance_no_show
BookingAttendanceStatus.Booked -> R.drawable.ic_attendance_booked
BookingAttendanceStatus.CheckedIn -> R.drawable.ic_attendance_checked_in
BookingAttendanceStatus.NoShow -> R.drawable.ic_attendance_no_show
else -> null
}

Expand All @@ -151,7 +151,7 @@ private fun BookingAttendanceStatusBottomSheetPreview() {
private fun AttendanceStatusRowPreview() {
WooThemeWithBackground {
AttendanceStatusRow(
status = BookingAttendanceStatus.CHECKED_IN,
status = BookingAttendanceStatus.CheckedIn,
onClick = {}
)
}
Expand Down
Loading