@@ -262,6 +262,39 @@ extension BookingDetailsViewModel {
262262 }
263263}
264264
265+ /// Cancel booking
266+ extension BookingDetailsViewModel {
267+ var isBookingCancellable : Bool {
268+ let ineligibleStatuses : [ BookingStatus ] = [ . cancelled, . complete, . unknown]
269+ return !ineligibleStatuses. contains ( booking. bookingStatus)
270+ }
271+
272+ @MainActor
273+ func cancelBooking( ) async throws {
274+ try await withCheckedThrowingContinuation { ( continuation: CheckedContinuation < Void , Error > ) in
275+ stores. dispatch ( BookingAction . cancelBooking ( siteID: booking. siteID, bookingID: booking. bookingID) { error in
276+ if let error {
277+ continuation. resume ( throwing: error)
278+ } else {
279+ continuation. resume ( returning: ( ) )
280+ }
281+ } )
282+ }
283+ }
284+
285+ func displayBookingCancellationErrorNotice( onRetry: @escaping ( ) -> Void ) {
286+ let text = String . localizedStringWithFormat (
287+ Localization . bookingCancellationFailedMessage,
288+ booking. bookingID
289+ )
290+ self . notice = Notice (
291+ message: text,
292+ feedbackType: . error,
293+ actionTitle: Localization . retryActionTitle
294+ ) { onRetry ( ) }
295+ }
296+ }
297+
265298private extension BookingDetailsViewModel {
266299 @MainActor
267300 func fetchResource( ) async -> BookingResource ? {
@@ -302,22 +335,17 @@ private extension BookingDetailsViewModel {
302335
303336extension BookingDetailsViewModel {
304337 var cancellationAlertMessage : String {
305- let productName = booking. orderInfo? . productInfo? . name ?? " "
338+ let productName = booking. productName ?? " "
339+ let customerName = booking. customerName
306340
307- let customerName : String = {
308- guard let address = booking. orderInfo? . customerInfo? . billingAddress else {
309- return " "
310- }
311- return [ address. firstName, address. lastName]
312- . compactMap { $0 }
313- . joined ( separator: " " )
314- } ( )
341+ guard productName. isNotEmpty, customerName. isNotEmpty else {
342+ return Localization . cancelBookingAlertGenericMessage
343+ }
315344
316345 let date = booking. startDate. formatted (
317346 date: . long,
318347 time: . shortened
319348 )
320-
321349 return String (
322350 format: Localization . cancelBookingAlertMessage,
323351 customerName,
@@ -390,6 +418,12 @@ private extension BookingDetailsViewModel {
390418 comment: " Message for the booking cancellation confirmation alert. %1$@ is customer name, %2$@ is product name, %3$@ is booking date. "
391419 )
392420
421+ static let cancelBookingAlertGenericMessage = NSLocalizedString (
422+ " BookingDetailsView.cancelation.alert.genericMessage " ,
423+ value: " Are you sure you want to cancel this booking? " ,
424+ comment: " Generic message for the booking cancellation confirmation alert. "
425+ )
426+
393427 static let bookingAttendanceStatusUpdateFailedMessage = NSLocalizedString (
394428 " BookingDetailsView.attendanceStatus.updateFailed.message " ,
395429 value: " Unable to change attendance status of Booking #%1$d " ,
@@ -398,6 +432,14 @@ private extension BookingDetailsViewModel {
398432 + " Parameters: %1$d - Booking number "
399433 )
400434
435+ static let bookingCancellationFailedMessage = NSLocalizedString (
436+ " BookingDetailsView.cancellation.failureMessage " ,
437+ value: " Unable to cancel Booking #%1$d " ,
438+ comment: " Content of error presented when cancelling a Booking fails. "
439+ + " It reads: Unable cancel Booking #{Booking number}. "
440+ + " Parameters: %1$d - Booking number "
441+ )
442+
401443 static let retryActionTitle = NSLocalizedString (
402444 " BookingDetailsView.retry.action " ,
403445 value: " Retry " ,
0 commit comments