Skip to content

Commit 3667e1b

Browse files
authored
Merge branch 'trunk' into fix/coupon_scrolling
2 parents b09a172 + a2fee5b commit 3667e1b

File tree

9 files changed

+169
-91
lines changed

9 files changed

+169
-91
lines changed

WooCommerce/Classes/Bookings/BookingList/BookingListContainerView.swift

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,37 @@ import struct Yosemite.Booking
33

44
struct BookingListContainerView: View {
55
@ObservedObject private var viewModel: BookingListContainerViewModel
6+
@Binding var selectedBooking: Booking?
67

7-
init(viewModel: BookingListContainerViewModel) {
8+
init(viewModel: BookingListContainerViewModel, selectedBooking: Binding<Booking?>) {
89
self.viewModel = viewModel
10+
self._selectedBooking = selectedBooking
911
}
1012

1113
var body: some View {
12-
NavigationStack {
13-
VStack(spacing: 0) {
14-
headerView
15-
TabView(selection: $viewModel.selectedTab) {
16-
ForEach(BookingListTab.allCases, id: \.rawValue) { tab in
17-
BookingListView(viewModel: viewModel.listViewModel(for: tab))
18-
.tag(tab)
19-
}
14+
VStack(spacing: 0) {
15+
headerView
16+
TabView(selection: $viewModel.selectedTab) {
17+
ForEach(BookingListTab.allCases, id: \.rawValue) { tab in
18+
BookingListView(
19+
viewModel: viewModel.listViewModel(for: tab),
20+
selectedBooking: $selectedBooking
21+
)
22+
.tag(tab)
2023
}
21-
.tabViewStyle(.page(indexDisplayMode: .never))
2224
}
23-
.navigationTitle(Localization.viewTitle)
24-
.toolbar {
25-
ToolbarItem(placement: .confirmationAction) {
26-
Button {
27-
// TODO
28-
} label: {
29-
Image(systemName: "magnifyingglass")
30-
}
25+
.tabViewStyle(.page(indexDisplayMode: .never))
26+
}
27+
.navigationTitle(Localization.viewTitle)
28+
.toolbar {
29+
ToolbarItem(placement: .confirmationAction) {
30+
Button {
31+
// TODO
32+
} label: {
33+
Image(systemName: "magnifyingglass")
3134
}
3235
}
3336
}
34-
.navigationDestination(for: Booking.self) { booking in
35-
let viewModel = BookingDetailsViewModel(booking: booking)
36-
BookingDetailsView(viewModel)
37-
}
3837
}
3938
}
4039

WooCommerce/Classes/Bookings/BookingList/BookingListView.swift

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import struct Yosemite.Booking
33

44
struct BookingListView: View {
55
@ObservedObject private var viewModel: BookingListViewModel
6+
@Binding var selectedBooking: Booking?
67

7-
init(viewModel: BookingListViewModel) {
8+
init(viewModel: BookingListViewModel, selectedBooking: Binding<Booking?>) {
89
self.viewModel = viewModel
10+
self._selectedBooking = selectedBooking
911
}
1012

1113
var body: some View {
@@ -31,22 +33,20 @@ struct BookingListView: View {
3133

3234
private extension BookingListView {
3335
var bookingList: some View {
34-
ScrollView {
35-
LazyVStack(spacing: 0) {
36-
ForEach(viewModel.bookings) { item in
37-
NavigationLink(value: item) {
38-
bookingItem(item)
39-
}
40-
.buttonStyle(.plain)
41-
}
42-
43-
InfiniteScrollIndicator(showContent: viewModel.shouldShowBottomActivityIndicator)
44-
.padding(.top, Layout.viewPadding)
45-
.onAppear {
46-
viewModel.onLoadNextPageAction()
47-
}
36+
List(selection: $selectedBooking) {
37+
ForEach(viewModel.bookings) { item in
38+
bookingItem(item)
39+
.tag(item)
4840
}
41+
42+
InfiniteScrollIndicator(showContent: viewModel.shouldShowBottomActivityIndicator)
43+
.padding(.top, Layout.viewPadding)
44+
.onAppear {
45+
viewModel.onLoadNextPageAction()
46+
}
4947
}
48+
.listStyle(.plain)
49+
.accentColor(Color(.listSelectedBackground))
5050
.refreshable {
5151
await viewModel.onRefreshAction()
5252
}
@@ -61,6 +61,7 @@ private extension BookingListView {
6161
.font(.body)
6262
.fontWeight(.medium)
6363
.frame(maxWidth: .infinity, alignment: .leading)
64+
.foregroundStyle(Color.primary)
6465

6566
// TODO: fetch bookable products & customer to get names or wait for API update
6667
Text(String(format: "%@ • %@", "Women's Hair cut", "Marianne"))
@@ -76,17 +77,13 @@ private extension BookingListView {
7677
Spacer()
7778
}
7879
}
79-
.padding(Layout.viewPadding)
80-
81-
Divider()
82-
.padding(.leading, Layout.viewPadding)
8380
}
84-
.background(Color(.listForeground(modal: false))) // TODO: update selected background color as part of selection handling
8581
}
8682

8783
func statusBadge(text: String, color: Color) -> some View {
8884
Text(text)
8985
.font(.caption2)
86+
.foregroundStyle(Color.primary)
9087
.padding(.horizontal, 8)
9188
.padding(.vertical, 4)
9289
.background(color.clipShape(RoundedRectangle(cornerRadius: 4)))

WooCommerce/Classes/Bookings/BookingsTabView.swift

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import SwiftUI
2+
import struct Yosemite.Booking
23

34
/// Hosting view for `BookingsTabView`
45
///
@@ -37,19 +38,24 @@ private extension BookingsTabViewHostingController {
3738
/// Main content of the Bookings tab
3839
///
3940
struct BookingsTabView: View {
40-
@State private var visibility: NavigationSplitViewVisibility = .all
41-
42-
private let siteID: Int64
41+
@State private var selectedBooking: Booking?
42+
@State private var visibility: NavigationSplitViewVisibility = .automatic
43+
@StateObject private var bookingListContainerViewModel: BookingListContainerViewModel
4344

4445
init(siteID: Int64) {
45-
self.siteID = siteID
46+
_bookingListContainerViewModel = StateObject(wrappedValue: BookingListContainerViewModel(siteID: siteID))
4647
}
4748

4849
var body: some View {
4950
NavigationSplitView(columnVisibility: $visibility) {
50-
BookingListContainerView(viewModel: BookingListContainerViewModel(siteID: siteID))
51+
BookingListContainerView(viewModel: bookingListContainerViewModel, selectedBooking: $selectedBooking)
5152
} detail: {
52-
Text("Booking Detail Screen")
53+
if let selectedBooking {
54+
let viewModel = BookingDetailsViewModel(booking: selectedBooking)
55+
BookingDetailsView(viewModel)
56+
} else {
57+
Text("Select a booking to see details.")
58+
}
5359
}
5460
.navigationSplitViewStyle(.balanced)
5561
}

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(

0 commit comments

Comments
 (0)