Skip to content

Commit ddfaa9f

Browse files
[Bookings][Part 6] Booking cancellation dialogue and currency formatting (#16209)
2 parents 298385a + 4823ccd commit ddfaa9f

File tree

6 files changed

+113
-34
lines changed

6 files changed

+113
-34
lines changed

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

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Foundation
22
import struct Networking.Booking
3-
import class WooFoundationCore.CurrencyFormatter
43

54
extension BookingDetailsViewModel {
65
struct AppointmentDetailsContent {
@@ -34,7 +33,10 @@ extension BookingDetailsViewModel {
3433
to: booking.endDate
3534
)
3635
),
37-
Row(title: Localization.appointmentDetailsPriceTitle, value: Self.formatPrice(booking.cost))
36+
Row(
37+
title: Localization.appointmentDetailsPriceTitle,
38+
value: BookingDetailsViewModel.formatPrice(for: booking, priceString: booking.cost)
39+
)
3840
]
3941
}
4042
}
@@ -51,15 +53,6 @@ private extension BookingDetailsViewModel.AppointmentDetailsContent {
5153
static func formatDuration(from startDate: Date, to endDate: Date) -> String {
5254
durationFormatter.string(from: startDate, to: endDate) ?? ""
5355
}
54-
55-
static func formatPrice(_ price: String) -> String {
56-
guard let decimalPrice = Decimal(string: price) else {
57-
return price
58-
}
59-
return CurrencyFormatter(
60-
currencySettings: ServiceLocator.currencySettings
61-
).formatAmount(decimalPrice) ?? price
62-
}
6356
}
6457

6558
private extension BookingDetailsViewModel.AppointmentDetailsContent {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Foundation
2+
import struct Networking.Booking
3+
import class WooFoundationCore.CurrencyFormatter
4+
import class WooFoundationCore.CurrencySettings
5+
import enum WooFoundationCore.CurrencyCode
6+
7+
extension BookingDetailsViewModel {
8+
static func formatPrice(for booking: Booking, priceString: String) -> String {
9+
guard let decimalPrice = Decimal(string: priceString) else {
10+
return priceString
11+
}
12+
return CurrencyFormatter(
13+
currencySettings: Self.currencySettings(for: booking)
14+
).formatAmount(decimalPrice) ?? priceString
15+
}
16+
17+
private static func currencySettings(for booking: Booking) -> CurrencySettings {
18+
let siteCurrencySettings = ServiceLocator.currencySettings
19+
guard let currencyCode = CurrencyCode(rawValue: booking.currency) else {
20+
return siteCurrencySettings
21+
}
22+
23+
return CurrencySettings(
24+
currencyCode: currencyCode,
25+
currencyPosition: siteCurrencySettings.currencyPosition,
26+
thousandSeparator: siteCurrencySettings.groupingSeparator,
27+
decimalSeparator: siteCurrencySettings.decimalSeparator,
28+
numberOfDecimals: siteCurrencySettings.fractionDigits
29+
)
30+
}
31+
}

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ final class BookingDetailsViewModel: ObservableObject {
55
let sections: [Section]
66
let navigationTitle: String
77

8+
private let booking: Booking
9+
810
init(booking: Booking) {
11+
self.booking = booking
12+
913
navigationTitle = Self.navigationTitle(for: booking)
1014

1115
let headerSection = Section.init(
@@ -59,6 +63,27 @@ final class BookingDetailsViewModel: ObservableObject {
5963
}
6064
}
6165

66+
extension BookingDetailsViewModel {
67+
var cancellationAlertMessage: String {
68+
// Temporary hardcoded
69+
//TODO: - replace with associated customer data
70+
let productName = "Women's Haircut"
71+
let customerName = "Margarita Nikolaevna"
72+
73+
let date = booking.startDate.formatted(
74+
date: .long,
75+
time: .shortened
76+
)
77+
78+
return String(
79+
format: Localization.cancelBookingAlertMessage,
80+
customerName,
81+
productName,
82+
date
83+
)
84+
}
85+
}
86+
6287
private extension BookingDetailsViewModel {
6388
static func navigationTitle(for booking: Booking) -> String {
6489
let titleFormat = NSLocalizedString(
@@ -107,5 +132,11 @@ private extension BookingDetailsViewModel {
107132
value: "Booking notes",
108133
comment: "Header title for the 'Booking notes' section in the booking details screen."
109134
)
135+
136+
static let cancelBookingAlertMessage = NSLocalizedString(
137+
"BookingDetailsView.cancelation.alert.message",
138+
value: "%1$@ will no longer be able to attend “%2$@” on %3$@.",
139+
comment: "Message for the booking cancellation confirmation alert. %1$@ is customer name, %2$@ is product name, %3$@ is booking date."
140+
)
110141
}
111142
}

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

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,21 @@ extension BookingDetailsViewModel {
99

1010
init(booking: Booking) {
1111
amounts = [
12-
.init(value: Self.formatPrice(booking.cost), type: .service),
13-
.init(value: Self.formatPrice("0"), type: .tax),
12+
.init(value: BookingDetailsViewModel.formatPrice(for: booking, priceString: booking.cost), type: .service),
13+
.init(value: BookingDetailsViewModel.formatPrice(for: booking, priceString: "0"), type: .tax),
1414
.init(value: "-", type: .discount),
15-
.init(value: Self.formatPrice(booking.cost), type: .total, emphasized: true),
15+
.init(value: BookingDetailsViewModel.formatPrice(for: booking, priceString: booking.cost), type: .total, emphasized: true),
1616
]
1717

1818
actions = [
1919
.markAsPaid,
20+
.issueRefund,
2021
.viewOrder
2122
]
2223
}
2324
}
2425
}
2526

26-
private extension BookingDetailsViewModel.PaymentContent {
27-
static func formatPrice(_ price: String) -> String {
28-
guard let decimalPrice = Decimal(string: price) else {
29-
return price
30-
}
31-
return CurrencyFormatter(
32-
currencySettings: ServiceLocator.currencySettings
33-
).formatAmount(decimalPrice) ?? price
34-
}
35-
}
36-
3727
extension BookingDetailsViewModel.PaymentContent {
3828
struct Amount {
3929
enum AmountType {
@@ -83,7 +73,7 @@ extension BookingDetailsViewModel.PaymentContent.Amount.AmountType {
8373
extension BookingDetailsViewModel.PaymentContent {
8474
enum Action: String, Identifiable {
8575
case markAsPaid
86-
case markAsRefunded
76+
case issueRefund
8777
case viewOrder
8878

8979
var id: String {
@@ -97,8 +87,8 @@ extension BookingDetailsViewModel.PaymentContent.Action {
9787
switch self {
9888
case .markAsPaid:
9989
return Localization.paymentMarkAsPaidButtonTitle
100-
case .markAsRefunded:
101-
return Localization.paymentMarkAsRefundedButtonTitle
90+
case .issueRefund:
91+
return Localization.paymentIssueRefundButtonTitle
10292
case .viewOrder:
10393
return Localization.paymentViewOrderButtonTitle
10494
}
@@ -108,7 +98,7 @@ extension BookingDetailsViewModel.PaymentContent.Action {
10898
switch self {
10999
case .markAsPaid:
110100
return true
111-
case .markAsRefunded, .viewOrder:
101+
case .issueRefund, .viewOrder:
112102
return false
113103
}
114104
}
@@ -145,10 +135,10 @@ private enum Localization {
145135
comment: "Title for 'Mark as paid' button in payment section in booking details view."
146136
)
147137

148-
static let paymentMarkAsRefundedButtonTitle = NSLocalizedString(
149-
"BookingDetailsView.payment.markAsRefunded.title",
150-
value: "Mark as refunded",
151-
comment: "Title for 'Mark as refunded' button in payment section in booking details view."
138+
static let paymentIssueRefundButtonTitle = NSLocalizedString(
139+
"BookingDetailsView.payment.issueRefund.title",
140+
value: "Issue refund",
141+
comment: "Title for 'Issue refund' button in payment section in booking details view."
152142
)
153143

154144
static let paymentViewOrderButtonTitle = NSLocalizedString(

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

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ struct BookingDetailsView: View {
66
@Environment(\.dismiss) private var dismiss
77
@State private var showingOptions = false
88
@State private var showingStatusSheet = false
9+
@State private var showingCancelAlert = false
910

1011
@ObservedObject private var viewModel: BookingDetailsViewModel
1112

@@ -78,6 +79,17 @@ struct BookingDetailsView: View {
7879
.presentationDetents([.medium, .large])
7980
.presentationDragIndicator(.visible)
8081
}
82+
.alert(
83+
Localization.cancelBookingAlertTitle,
84+
isPresented: $showingCancelAlert
85+
) {
86+
Button(Localization.cancelBookingAlertCancelAction, role: .cancel) {}
87+
Button(Localization.cancelBookingAlertConfirmAction, role: .destructive) {
88+
print("On cancel booking confirmation tap")
89+
}
90+
} message: {
91+
Text(viewModel.cancellationAlertMessage)
92+
}
8193
}
8294
}
8395

@@ -185,7 +197,7 @@ private extension BookingDetailsView {
185197
}
186198

187199
Button {
188-
/// On cancel booking button tap
200+
showingCancelAlert = true
189201
} label: {
190202
Text(Localization.cancelBooking)
191203
}
@@ -356,6 +368,24 @@ private extension BookingDetailsView {
356368
comment: "'Cancel booking' button title in appointment details section in booking details view."
357369
)
358370

371+
static let cancelBookingAlertTitle = NSLocalizedString(
372+
"BookingDetailsView.cancelation.alert.title",
373+
value: "Cancel booking",
374+
comment: "Title for the booking cancellation confirmation alert."
375+
)
376+
377+
static let cancelBookingAlertConfirmAction = NSLocalizedString(
378+
"BookingDetailsView.cancelation.alert.confirmAction",
379+
value: "Yes, cancel it",
380+
comment: "Confirm button title for the booking cancellation confirmation alert."
381+
)
382+
383+
static let cancelBookingAlertCancelAction = NSLocalizedString(
384+
"BookingDetailsView.cancelation.alert.cancelAction",
385+
value: "No, keep it",
386+
comment: "Cancel button title for the booking cancellation confirmation alert."
387+
)
388+
359389
/// Attendance section
360390
static let statusRowTitle = NSLocalizedString(
361391
"BookingDetailsView.customer.status.title",

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,7 @@
984984
26FE09E124DB8FA000B9BDF5 /* SurveyCoordinatorControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26FE09E024DB8FA000B9BDF5 /* SurveyCoordinatorControllerTests.swift */; };
985985
26FFC50C2BED7C5A0067B3A4 /* WatchDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B249702BEC801400730730 /* WatchDependencies.swift */; };
986986
26FFC50D2BED7C5B0067B3A4 /* WatchDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26B249702BEC801400730730 /* WatchDependencies.swift */; };
987+
2D05337E2E951A62004111FD /* BookingDetailsViewModel+PriceFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D05337D2E951A62004111FD /* BookingDetailsViewModel+PriceFormatting.swift */; };
987988
2D05D19F2E82D1A8004111FD /* BookingDetailsViewModel+Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D05D19E2E82D1A3004111FD /* BookingDetailsViewModel+Section.swift */; };
988989
2D05D1A22E82D235004111FD /* HeaderContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D05D1A12E82D233004111FD /* HeaderContent.swift */; };
989990
2D05D1A42E82D266004111FD /* AppointmentDetailsContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D05D1A32E82D25F004111FD /* AppointmentDetailsContent.swift */; };
@@ -3877,6 +3878,7 @@
38773878
26FE09E024DB8FA000B9BDF5 /* SurveyCoordinatorControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurveyCoordinatorControllerTests.swift; sourceTree = "<group>"; };
38783879
26FFD32628C6A0A4002E5E5E /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
38793880
26FFD32928C6A0F4002E5E5E /* UIImage+Widgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Widgets.swift"; sourceTree = "<group>"; };
3881+
2D05337D2E951A62004111FD /* BookingDetailsViewModel+PriceFormatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BookingDetailsViewModel+PriceFormatting.swift"; sourceTree = "<group>"; };
38803882
2D05D19E2E82D1A3004111FD /* BookingDetailsViewModel+Section.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BookingDetailsViewModel+Section.swift"; sourceTree = "<group>"; };
38813883
2D05D1A02E82D1EF004111FD /* BookingDetailsViewModel+SectionContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BookingDetailsViewModel+SectionContent.swift"; sourceTree = "<group>"; };
38823884
2D05D1A12E82D233004111FD /* HeaderContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderContent.swift; sourceTree = "<group>"; };
@@ -7944,6 +7946,7 @@
79447946
2D05D1A62E82D49D004111FD /* BookingDetailsViewModel+Status.swift */,
79457947
2D05D19E2E82D1A3004111FD /* BookingDetailsViewModel+Section.swift */,
79467948
2D05D1A02E82D1EF004111FD /* BookingDetailsViewModel+SectionContent.swift */,
7949+
2D05337D2E951A62004111FD /* BookingDetailsViewModel+PriceFormatting.swift */,
79477950
);
79487951
path = "Booking Details";
79497952
sourceTree = "<group>";
@@ -14919,6 +14922,7 @@
1491914922
45CE2D322625AA9A00E3CA00 /* ShippingLabelPackageList.swift in Sources */,
1492014923
AEFF77A42978389400667F7A /* PriceInputViewController.swift in Sources */,
1492114924
02535CBB25823F7A00E137BB /* ShippingLabelPaperSize+UI.swift in Sources */,
14925+
2D05337E2E951A62004111FD /* BookingDetailsViewModel+PriceFormatting.swift in Sources */,
1492214926
CCD2E67E25DD4DC900BD975D /* ProductVariationsViewModel.swift in Sources */,
1492314927
02C2756824F4E77F00286C04 /* ProductShippingSettingsViewModel.swift in Sources */,
1492414928
E12AF69926BA8ADC00C371C1 /* CardPresentPaymentsOnboardingUseCase.swift in Sources */,

0 commit comments

Comments
 (0)