Skip to content

Commit cf01d8c

Browse files
authored
[Woo POS][Historical Orders] CfT fixes (#16216)
2 parents 892e677 + ebae30f commit cf01d8c

File tree

9 files changed

+127
-51
lines changed

9 files changed

+127
-51
lines changed

Modules/Sources/PointOfSale/Presentation/Orders/POSOrderDetailsView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ private extension POSOrderDetailsView {
312312

313313
@ViewBuilder
314314
func refundsSection(_ order: POSOrder) -> some View {
315-
ForEach(order.refunds, id: \.refundID) { refund in
315+
ForEach(order.refunds.sorted(by: { $0.refundID < $1.refundID }), id: \.refundID) { refund in
316316
refundRow(refund: refund)
317317
divider
318318
}

Modules/Sources/PointOfSale/Presentation/Orders/POSOrderListEmptyViewModel.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct POSOrderListEmptyViewModel: POSListEmptyViewModelProtocol {
2121
}
2222

2323
var icon: Image {
24-
PointOfSaleAssets.magnifierNotFound.decorativeImage
24+
isSearching ? PointOfSaleAssets.magnifierNotFound.decorativeImage : PointOfSaleAssets.noOrders.decorativeImage
2525
}
2626
}
2727

@@ -33,15 +33,15 @@ private enum Localization {
3333
)
3434

3535
static let emptyOrdersSubtitle = NSLocalizedString(
36-
"pos.orderListView.emptyOrdersSubtitle",
37-
value: "Orders will appear here once you start processing sales on the POS.",
36+
"pos.orderListView.emptyOrdersSubtitle2",
37+
value: "Explore how you can increase your store sales.",
3838
comment: "Subtitle appearing when there are no orders to display."
3939
)
4040

4141
static let emptyOrdersButtonTitle = NSLocalizedString(
42-
"pos.orderListView.emptyOrdersButtonTitle",
43-
value: "Refresh",
44-
comment: "Button text for refreshing orders when list is empty."
42+
"pos.orderListView.emptyOrdersButtonTitle2",
43+
value: "Learn more",
44+
comment: "Button text for opening an information view when orders when list is empty."
4545
)
4646

4747
static let emptyOrdersSearchTitle = NSLocalizedString(

Modules/Sources/PointOfSale/Presentation/Orders/POSOrderListView.swift

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,15 @@ import struct Yosemite.POSOrder
44
import enum Yosemite.OrderPaymentMethod
55

66
struct POSOrderListView: View {
7+
@Binding var isSearching: Bool
8+
@Binding var searchTerm: String
79
let onClose: () -> Void
810

911
@Environment(POSOrderListModel.self) private var orderListModel
10-
@Environment(\.keyboardObserver) private var keyboardObserver
1112
@Environment(\.posAnalytics) private var analytics
1213
@Environment(\.siteTimezone) private var siteTimezone
1314
@StateObject private var infiniteScrollTriggerDeterminer = ThresholdInfiniteScrollTriggerDeterminer()
1415

15-
@State private var isSearching: Bool = false
16-
@State private var searchTerm: String = ""
17-
@Namespace private var searchTransition
18-
1916
private var ordersViewState: POSOrderListState {
2017
orderListModel.ordersController.ordersViewState
2118
}
@@ -46,8 +43,11 @@ struct POSOrderListView: View {
4643
setSearch(true)
4744
}
4845
.accessibilityLabel(Localization.searchButtonAccessibilityLabel)
49-
.matchedGeometryEffect(id: Constants.searchControlID, in: searchTransition)
50-
.transition(.opacity.combined(with: .scale))
46+
.transition(.asymmetric(
47+
insertion: .scale.combined(with: .opacity)
48+
.animation(.easeInOut(duration: Constants.animationDuration).delay(Constants.animationDuration)),
49+
removal: .opacity.animation(.easeInOut(duration: Constants.animationDuration * 0.5))
50+
))
5151
}
5252

5353
if isSearching {
@@ -59,8 +59,12 @@ struct POSOrderListView: View {
5959
}
6060
)
6161
.posSearchTextFieldUnfocusedBorderColor(.posOutlineVariant)
62-
.matchedGeometryEffect(id: Constants.searchControlID, in: searchTransition)
63-
.transition(.opacity.combined(with: .move(edge: .leading)))
62+
.transition(.asymmetric(
63+
insertion: .opacity.combined(with: .move(edge: .trailing))
64+
.animation(.easeInOut(duration: Constants.animationDuration).delay(Constants.animationDuration * 0.5)),
65+
removal: .opacity.combined(with: .move(edge: .trailing))
66+
.animation(.easeInOut(duration: Constants.animationDuration))
67+
))
6468
.onChange(of: searchTerm) { _, newValue in
6569
if newValue.isEmpty {
6670
orderListModel.ordersController.clearSearchOrders()
@@ -71,23 +75,21 @@ struct POSOrderListView: View {
7175
)
7276
.animation(.easeInOut(duration: Constants.animationDuration), value: isSearching)
7377

74-
switch ordersViewState {
75-
case .empty:
78+
switch (ordersViewState, isSearching) {
79+
case (.empty, true):
7680
POSListEmptyView(
77-
viewModel: POSOrderListEmptyViewModel(isSearching: isSearching)
81+
viewModel: POSOrderListEmptyViewModel(isSearching: true)
7882
) {
7983
Task { @MainActor in
8084
await orderListModel.ordersController.loadOrders()
8185
}
8286
}
83-
.padding(.bottom, keyboardObserver.keyboardHeight)
84-
case .error(let errorState):
87+
case (.error(let errorState), true):
8588
POSListErrorView(error: errorState) {
8689
Task { @MainActor in
8790
await orderListModel.ordersController.loadOrders()
8891
}
8992
}
90-
.padding(.bottom, keyboardObserver.keyboardHeight)
9193
default:
9294
listView
9395
}
@@ -99,9 +101,6 @@ struct POSOrderListView: View {
99101
analytics.track(event: WooAnalyticsEvent.PointOfSale.ordersListPullToRefresh())
100102
await orderListModel.ordersController.refreshOrders()
101103
}
102-
.task {
103-
await orderListModel.ordersController.loadOrders()
104-
}
105104
}
106105

107106
@ViewBuilder
@@ -128,7 +127,7 @@ struct POSOrderListView: View {
128127
await orderListModel.ordersController.loadNextOrders()
129128
},
130129
content: {
131-
LazyVStack(spacing: POSSpacing.small) {
130+
LazyVStack(spacing: POSSpacing.medium) {
132131
headerRows
133132
.id(Constants.scrollTopID)
134133

@@ -384,7 +383,6 @@ private enum Constants {
384383
static let orderCardMinHeight: CGFloat = 112
385384
static let maximumOrderCardHeight: CGFloat = Constants.orderCardMinHeight * 2
386385
static let animationDuration: CGFloat = 0.2
387-
static let searchControlID = "searchControl"
388386
static let scrollTopID = "orderListViewTopID"
389387
}
390388

@@ -450,7 +448,7 @@ private enum Localization {
450448
#if DEBUG
451449
#Preview("List") {
452450
NavigationSplitView(columnVisibility: .constant(.all)) {
453-
POSOrderListView(onClose: {})
451+
POSOrderListView(isSearching: .constant(false), searchTerm: .constant(""), onClose: {})
454452
.navigationSplitViewColumnWidth(450)
455453
.environment(POSPreviewHelpers.makePreviewOrdersModel(state: POSPreviewHelpers.loadedState()))
456454
} detail: {
@@ -459,19 +457,19 @@ private enum Localization {
459457
.navigationSplitViewStyle(.balanced)
460458
}
461459

462-
#Preview("Empty State") {
460+
#Preview("Empty State in Search") {
463461
NavigationSplitView(columnVisibility: .constant(.all)) {
464-
POSOrderListView(onClose: {})
462+
POSOrderListView(isSearching: .constant(true), searchTerm: .constant(""), onClose: {})
465463
.navigationSplitViewColumnWidth(450)
466464
.environment(POSPreviewHelpers.makePreviewOrdersModel(state: .empty))
467465
} detail: {
468466
Text("Detail View")
469467
}
470468
}
471469

472-
#Preview("Error State") {
470+
#Preview("Error State in Search") {
473471
NavigationSplitView(columnVisibility: .constant(.all)) {
474-
POSOrderListView(onClose: {})
472+
POSOrderListView(isSearching: .constant(true), searchTerm: .constant(""), onClose: {})
475473
.navigationSplitViewColumnWidth(450)
476474
.environment(POSPreviewHelpers.makePreviewOrdersModel(state: .error(.errorOnLoadingOrders())))
477475
} detail: {
@@ -481,7 +479,7 @@ private enum Localization {
481479

482480
#Preview("Loading State") {
483481
NavigationSplitView(columnVisibility: .constant(.all)) {
484-
POSOrderListView(onClose: {})
482+
POSOrderListView(isSearching: .constant(false), searchTerm: .constant(""), onClose: {})
485483
.navigationSplitViewColumnWidth(450)
486484
.environment(POSPreviewHelpers.makePreviewOrdersModel(state: .loading([])))
487485
} detail: {
@@ -491,7 +489,7 @@ private enum Localization {
491489

492490
#Preview("Inline Error - Refresh") {
493491
NavigationSplitView(columnVisibility: .constant(.all)) {
494-
POSOrderListView(onClose: {})
492+
POSOrderListView(isSearching: .constant(false), searchTerm: .constant(""), onClose: {})
495493
.navigationSplitViewColumnWidth(450)
496494
.environment(POSPreviewHelpers.makePreviewOrdersModel(
497495
state: .inlineError(POSPreviewHelpers.makePreviewOrders(),
@@ -505,7 +503,7 @@ private enum Localization {
505503

506504
#Preview("Inline Error - Pagination") {
507505
NavigationSplitView(columnVisibility: .constant(.all)) {
508-
POSOrderListView(onClose: {})
506+
POSOrderListView(isSearching: .constant(false), searchTerm: .constant(""), onClose: {})
509507
.navigationSplitViewColumnWidth(450)
510508
.environment(POSPreviewHelpers.makePreviewOrdersModel(
511509
state: .inlineError(POSPreviewHelpers.makePreviewOrders(),
@@ -519,7 +517,7 @@ private enum Localization {
519517

520518
#Preview("Search Empty State") {
521519
NavigationSplitView(columnVisibility: .constant(.all)) {
522-
POSOrderListView(onClose: {})
520+
POSOrderListView(isSearching: .constant(true), searchTerm: .constant(""), onClose: {})
523521
.navigationSplitViewColumnWidth(450)
524522
.environment(POSPreviewHelpers.makePreviewOrdersModel(state: .empty))
525523
} detail: {

Modules/Sources/PointOfSale/Presentation/Orders/POSOrdersView.swift

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
11
import SwiftUI
22
import UIKit
33
import struct WooFoundation.WooAnalyticsEvent
4+
import struct WooFoundation.SafariView
45

56
struct POSOrdersView: View {
67
@Binding var isPresented: Bool
78
@Environment(POSOrderListModel.self) private var orderListModel
89
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
910
@Environment(\.posAnalytics) private var analytics
11+
@State private var isSearching: Bool = false
12+
@State private var searchTerm: String = ""
13+
@State private var showBlog = false
1014

1115
var body: some View {
12-
switch orderListModel.ordersController.ordersViewState {
13-
case .error(let error):
16+
contentView
17+
.task {
18+
await orderListModel.ordersController.loadOrders()
19+
}
20+
}
21+
22+
@ViewBuilder
23+
private var contentView: some View {
24+
switch (orderListModel.ordersController.ordersViewState, isSearching) {
25+
case (.error(let error), false):
1426
errorView(error)
27+
case (.empty, false):
28+
emptyView()
1529
default:
1630
CustomNavigationSplitView(selection: Binding(
1731
get: { orderListModel.ordersController.selectedOrder },
1832
set: { orderListModel.ordersController.selectOrder($0) }
1933
)) { _ in
20-
POSOrderListView() {
34+
POSOrderListView(isSearching: $isSearching, searchTerm: $searchTerm) {
2135
isPresented = false
2236
}
2337
} detail: { selection in
@@ -65,20 +79,55 @@ struct POSOrdersView: View {
6579

6680
@ViewBuilder
6781
private func errorView(_ error: PointOfSaleErrorState) -> some View {
68-
VStack(spacing: 0) {
69-
POSPageHeaderView(
70-
title: POSOrderListView.Localization.ordersTitle,
71-
backButtonConfiguration: .init(state: .enabled, action: {
72-
isPresented = false
73-
}))
74-
.posHeaderBackButtonIcon(systemName: "xmark")
82+
ZStack {
83+
VStack {
84+
Spacer()
85+
POSListErrorView(error: error) {
86+
Task { @MainActor in
87+
await orderListModel.ordersController.loadOrders()
88+
}
89+
}
90+
Spacer()
91+
}
92+
93+
VStack {
94+
POSPageHeaderView(
95+
title: POSOrderListView.Localization.ordersTitle,
96+
backButtonConfiguration: .init(state: .enabled, action: {
97+
isPresented = false
98+
}))
99+
.posHeaderBackButtonIcon(systemName: "xmark")
100+
Spacer()
101+
}
102+
}
103+
}
75104

76-
POSListErrorView(error: error) {
77-
Task { @MainActor in
78-
await orderListModel.ordersController.loadOrders()
105+
@ViewBuilder
106+
private func emptyView() -> some View {
107+
ZStack {
108+
VStack {
109+
Spacer()
110+
POSListEmptyView(
111+
viewModel: POSOrderListEmptyViewModel(isSearching: false)
112+
) {
113+
showBlog = true
79114
}
115+
Spacer()
116+
}
117+
118+
VStack {
119+
POSPageHeaderView(
120+
title: POSOrderListView.Localization.ordersTitle,
121+
backButtonConfiguration: .init(state: .enabled, action: {
122+
isPresented = false
123+
}))
124+
.posHeaderBackButtonIcon(systemName: "xmark")
125+
Spacer()
80126
}
81127
}
128+
.posFullScreenCover(isPresented: $showBlog) {
129+
SafariView(url: POSConstants.URLs.wooCommerceBlog.asURL())
130+
}
82131
}
83132
}
84133

@@ -158,8 +207,18 @@ private enum Constants {
158207
}
159208

160209
#if DEBUG
161-
#Preview("Orders View") {
210+
#Preview("Orders View List") {
211+
POSOrdersView(isPresented: .constant(true))
212+
.environment(POSPreviewHelpers.makePreviewOrdersModel(state: POSPreviewHelpers.loadedState()))
213+
}
214+
215+
#Preview("Orders View Empty") {
162216
POSOrdersView(isPresented: .constant(true))
163217
.environment(POSPreviewHelpers.makePreviewOrdersModel(state: .empty))
164218
}
219+
220+
#Preview("Orders View Error") {
221+
POSOrdersView(isPresented: .constant(true))
222+
.environment(POSPreviewHelpers.makePreviewOrdersModel(state: .error(.errorOnLoadingOrders())))
223+
}
165224
#endif

Modules/Sources/PointOfSale/Presentation/PointOfSaleAssets.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ enum PointOfSaleAssets: CaseIterable {
2020
case netum1228BCHIDBarcode
2121
case netum1228BCPairBarcode
2222
case testEan13Barcode
23+
case noOrders
2324

2425
var image: Image {
2526
Image(imageName, bundle: .module)
@@ -67,6 +68,8 @@ enum PointOfSaleAssets: CaseIterable {
6768
"netum-1228bc-hid-barcode"
6869
case .netum1228BCPairBarcode:
6970
"netum-1228bc-pair-barcode"
71+
case .noOrders:
72+
"pos-no-orders"
7073
}
7174
}
7275
}

Modules/Sources/PointOfSale/Presentation/Reusable Views/POSListEmptyView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ struct POSListEmptyView: View {
6868
}, label: {
6969
Text(buttonTitle)
7070
})
71-
.buttonStyle(POSOutlinedButtonStyle(size: .normal))
71+
.buttonStyle(POSFilledButtonStyle(size: .normal))
7272
.frame(width: viewWidth / 2)
7373
.padding([.leading, .trailing])
7474
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "pos-no-orders.pdf",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
}
12+
}
Binary file not shown.

Modules/Sources/PointOfSale/Utils/POSConstants.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ enum POSConstants {
2020
///
2121
case inPersonPaymentsLearnMoreWCPay =
2222
"https://woocommerce.com/document/woocommerce-payments/in-person-payments/getting-started-with-in-person-payments/"
23+
24+
/// URL for WooCommerce blog
25+
///
26+
case wooCommerceBlog = "https://woocommerce.com/blog/"
2327
}
2428
}
2529

0 commit comments

Comments
 (0)