Skip to content

Commit d44e9b5

Browse files
[Bookings] Attendance status update failure notice and retry (#16286)
2 parents 29fbd68 + 3a69851 commit d44e9b5

File tree

3 files changed

+73
-3
lines changed

3 files changed

+73
-3
lines changed

WooCommerce/Classes/ViewModels/Booking Details/BookingDetailsViewModel.swift

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ final class BookingDetailsViewModel: ObservableObject {
2727

2828
@Published private(set) var navigationTitle = ""
2929
@Published private(set) var sections: [Section] = []
30+
@Published var notice: Notice?
3031

3132
var bookingAttendanceStatus: BookingAttendanceStatus {
3233
booking.attendanceStatus
@@ -233,14 +234,32 @@ extension BookingDetailsViewModel {
233234
siteID: booking.siteID,
234235
bookingID: booking.bookingID,
235236
status: newStatus
236-
) { error in
237-
if let error {
237+
) { [weak self] error in
238+
if let error, let self {
238239
DDLogError("⛔️ Error updating booking attendance status: \(error)")
239-
// TODO: Show an error notice to the user
240+
displayAttendanceStatusUpdatedErrorNotice(status: newStatus)
240241
}
241242
}
242243
stores.dispatch(action)
243244
}
245+
246+
private func displayAttendanceStatusUpdatedErrorNotice(status: BookingAttendanceStatus) {
247+
let text = String.localizedStringWithFormat(
248+
Localization.bookingAttendanceStatusUpdateFailedMessage,
249+
booking.bookingID
250+
)
251+
self.notice = Notice(
252+
message: text,
253+
feedbackType: .error,
254+
actionTitle: Localization.retryActionTitle
255+
) { [weak self] in
256+
guard let self else {
257+
return
258+
}
259+
260+
updateAttendanceStatus(to: status)
261+
}
262+
}
244263
}
245264

246265
private extension BookingDetailsViewModel {
@@ -362,5 +381,19 @@ private extension BookingDetailsViewModel {
362381
value: "%1$@ will no longer be able to attend “%2$@” on %3$@.",
363382
comment: "Message for the booking cancellation confirmation alert. %1$@ is customer name, %2$@ is product name, %3$@ is booking date."
364383
)
384+
385+
static let bookingAttendanceStatusUpdateFailedMessage = NSLocalizedString(
386+
"BookingDetailsView.attendanceStatus.updateFailed.message",
387+
value: "Unable to change attendance status of Booking #%1$d",
388+
comment: "Content of error presented when updating the attendance status of a Booking fails. "
389+
+ "It reads: Unable to change status of Booking #{Booking number}. "
390+
+ "Parameters: %1$d - Booking number"
391+
)
392+
393+
static let retryActionTitle = NSLocalizedString(
394+
"BookingDetailsView.retry.action",
395+
value: "Retry",
396+
comment: "Retry Action"
397+
)
365398
}
366399
}

WooCommerce/Classes/ViewRelated/Bookings/Booking Details/BookingDetailsView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ struct BookingDetailsView: View {
103103
Text(viewModel.cancellationAlertMessage)
104104
}
105105
.notice($notice)
106+
.notice($viewModel.notice)
106107
}
107108
}
108109

WooCommerce/WooCommerceTests/ViewRelated/Bookings/BookingDetailsViewModelTests.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,42 @@ final class BookingDetailsViewModelTests: XCTestCase {
247247
XCTAssertEqual(status, newStatus)
248248
}
249249

250+
func test_error_notice_displayed_when_attendance_staus_update_fails() {
251+
// Given
252+
let booking = Booking.fake()
253+
let viewModel = BookingDetailsViewModel(booking: booking, stores: storesManager)
254+
let newStatus = BookingAttendanceStatus.checkedIn
255+
enum TestError: Error { case generic }
256+
257+
// When
258+
viewModel.updateAttendanceStatus(to: newStatus)
259+
260+
// Then
261+
XCTAssertEqual(storesManager.receivedActions.count, 1)
262+
guard let action = storesManager.receivedActions.first as? BookingAction else {
263+
XCTFail("Incorrect action type dispatched")
264+
return
265+
}
266+
267+
guard case let .updateBookingAttendanceStatus(_, _, _, onCompletion) = action else {
268+
XCTFail("Incorrect action case dispatched")
269+
return
270+
}
271+
272+
onCompletion(TestError.generic)
273+
274+
XCTAssertNotNil(viewModel.notice)
275+
XCTAssertEqual(viewModel.notice?.feedbackType, .error)
276+
277+
let messageFormat = NSLocalizedString(
278+
"BookingDetailsView.attendanceStatus.updateFailed.message",
279+
value: "Unable to change attendance status of Booking #%1$d",
280+
comment: ""
281+
)
282+
let expectedMessage = String(format: messageFormat, booking.bookingID)
283+
XCTAssertEqual(viewModel.notice?.message, expectedMessage)
284+
}
285+
250286
func test_init_whenBookingHasStatusAndAttendanceStatus_updatesHeaderContentWithCorrectLocalizedStrings() {
251287
// Given
252288
let booking = Booking.fake().copy(

0 commit comments

Comments
 (0)