diff --git a/Modules/Sources/PointOfSale/Presentation/Orders/POSOrderListEmptyViewModel.swift b/Modules/Sources/PointOfSale/Presentation/Orders/POSOrderListEmptyViewModel.swift index c2e2693bbfb..b8184fcb0ba 100644 --- a/Modules/Sources/PointOfSale/Presentation/Orders/POSOrderListEmptyViewModel.swift +++ b/Modules/Sources/PointOfSale/Presentation/Orders/POSOrderListEmptyViewModel.swift @@ -21,7 +21,7 @@ struct POSOrderListEmptyViewModel: POSListEmptyViewModelProtocol { } var icon: Image { - PointOfSaleAssets.magnifierNotFound.decorativeImage + isSearching ? PointOfSaleAssets.magnifierNotFound.decorativeImage : PointOfSaleAssets.noOrders.decorativeImage } } @@ -33,15 +33,15 @@ private enum Localization { ) static let emptyOrdersSubtitle = NSLocalizedString( - "pos.orderListView.emptyOrdersSubtitle", - value: "Orders will appear here once you start processing sales on the POS.", + "pos.orderListView.emptyOrdersSubtitle2", + value: "Explore how you can increase your store sales.", comment: "Subtitle appearing when there are no orders to display." ) static let emptyOrdersButtonTitle = NSLocalizedString( - "pos.orderListView.emptyOrdersButtonTitle", - value: "Refresh", - comment: "Button text for refreshing orders when list is empty." + "pos.orderListView.emptyOrdersButtonTitle2", + value: "Learn more", + comment: "Button text for opening an information view when orders when list is empty." ) static let emptyOrdersSearchTitle = NSLocalizedString( diff --git a/Modules/Sources/PointOfSale/Presentation/Orders/POSOrderListView.swift b/Modules/Sources/PointOfSale/Presentation/Orders/POSOrderListView.swift index 37782c0f67c..3b036e58ee5 100644 --- a/Modules/Sources/PointOfSale/Presentation/Orders/POSOrderListView.swift +++ b/Modules/Sources/PointOfSale/Presentation/Orders/POSOrderListView.swift @@ -4,6 +4,7 @@ import struct Yosemite.POSOrder import enum Yosemite.OrderPaymentMethod struct POSOrderListView: View { + @Binding var isSearching: Bool let onClose: () -> Void @Environment(POSOrderListModel.self) private var orderListModel @@ -12,7 +13,6 @@ struct POSOrderListView: View { @Environment(\.siteTimezone) private var siteTimezone @StateObject private var infiniteScrollTriggerDeterminer = ThresholdInfiniteScrollTriggerDeterminer() - @State private var isSearching: Bool = false @State private var searchTerm: String = "" @Namespace private var searchTransition @@ -71,17 +71,17 @@ struct POSOrderListView: View { ) .animation(.easeInOut(duration: Constants.animationDuration), value: isSearching) - switch ordersViewState { - case .empty: + switch (ordersViewState, isSearching) { + case (.empty, true): POSListEmptyView( - viewModel: POSOrderListEmptyViewModel(isSearching: isSearching) + viewModel: POSOrderListEmptyViewModel(isSearching: true) ) { Task { @MainActor in await orderListModel.ordersController.loadOrders() } } .padding(.bottom, keyboardObserver.keyboardHeight) - case .error(let errorState): + case (.error(let errorState), true): POSListErrorView(error: errorState) { Task { @MainActor in await orderListModel.ordersController.loadOrders() @@ -450,7 +450,7 @@ private enum Localization { #if DEBUG #Preview("List") { NavigationSplitView(columnVisibility: .constant(.all)) { - POSOrderListView(onClose: {}) + POSOrderListView(isSearching: .constant(false), onClose: {}) .navigationSplitViewColumnWidth(450) .environment(POSPreviewHelpers.makePreviewOrdersModel(state: POSPreviewHelpers.loadedState())) } detail: { @@ -459,9 +459,9 @@ private enum Localization { .navigationSplitViewStyle(.balanced) } -#Preview("Empty State") { +#Preview("Empty State in Search") { NavigationSplitView(columnVisibility: .constant(.all)) { - POSOrderListView(onClose: {}) + POSOrderListView(isSearching: .constant(true), onClose: {}) .navigationSplitViewColumnWidth(450) .environment(POSPreviewHelpers.makePreviewOrdersModel(state: .empty)) } detail: { @@ -469,9 +469,9 @@ private enum Localization { } } -#Preview("Error State") { +#Preview("Error State in Search") { NavigationSplitView(columnVisibility: .constant(.all)) { - POSOrderListView(onClose: {}) + POSOrderListView(isSearching: .constant(true), onClose: {}) .navigationSplitViewColumnWidth(450) .environment(POSPreviewHelpers.makePreviewOrdersModel(state: .error(.errorOnLoadingOrders()))) } detail: { @@ -481,7 +481,7 @@ private enum Localization { #Preview("Loading State") { NavigationSplitView(columnVisibility: .constant(.all)) { - POSOrderListView(onClose: {}) + POSOrderListView(isSearching: .constant(false), onClose: {}) .navigationSplitViewColumnWidth(450) .environment(POSPreviewHelpers.makePreviewOrdersModel(state: .loading([]))) } detail: { @@ -491,7 +491,7 @@ private enum Localization { #Preview("Inline Error - Refresh") { NavigationSplitView(columnVisibility: .constant(.all)) { - POSOrderListView(onClose: {}) + POSOrderListView(isSearching: .constant(false), onClose: {}) .navigationSplitViewColumnWidth(450) .environment(POSPreviewHelpers.makePreviewOrdersModel( state: .inlineError(POSPreviewHelpers.makePreviewOrders(), @@ -505,7 +505,7 @@ private enum Localization { #Preview("Inline Error - Pagination") { NavigationSplitView(columnVisibility: .constant(.all)) { - POSOrderListView(onClose: {}) + POSOrderListView(isSearching: .constant(false), onClose: {}) .navigationSplitViewColumnWidth(450) .environment(POSPreviewHelpers.makePreviewOrdersModel( state: .inlineError(POSPreviewHelpers.makePreviewOrders(), @@ -519,7 +519,7 @@ private enum Localization { #Preview("Search Empty State") { NavigationSplitView(columnVisibility: .constant(.all)) { - POSOrderListView(onClose: {}) + POSOrderListView(isSearching: .constant(true), onClose: {}) .navigationSplitViewColumnWidth(450) .environment(POSPreviewHelpers.makePreviewOrdersModel(state: .empty)) } detail: { diff --git a/Modules/Sources/PointOfSale/Presentation/Orders/POSOrdersView.swift b/Modules/Sources/PointOfSale/Presentation/Orders/POSOrdersView.swift index 47f10cdd13b..421093fead4 100644 --- a/Modules/Sources/PointOfSale/Presentation/Orders/POSOrdersView.swift +++ b/Modules/Sources/PointOfSale/Presentation/Orders/POSOrdersView.swift @@ -7,17 +7,20 @@ struct POSOrdersView: View { @Environment(POSOrderListModel.self) private var orderListModel @Environment(\.horizontalSizeClass) private var horizontalSizeClass @Environment(\.posAnalytics) private var analytics + @State private var isSearching: Bool = false var body: some View { - switch orderListModel.ordersController.ordersViewState { - case .error(let error): + switch (orderListModel.ordersController.ordersViewState, isSearching) { + case (.error(let error), false): errorView(error) + case (.empty, false): + emptyView() default: CustomNavigationSplitView(selection: Binding( get: { orderListModel.ordersController.selectedOrder }, set: { orderListModel.ordersController.selectOrder($0) } )) { _ in - POSOrderListView() { + POSOrderListView(isSearching: $isSearching) { isPresented = false } } detail: { selection in @@ -65,18 +68,52 @@ struct POSOrdersView: View { @ViewBuilder private func errorView(_ error: PointOfSaleErrorState) -> some View { - VStack(spacing: 0) { - POSPageHeaderView( - title: POSOrderListView.Localization.ordersTitle, - backButtonConfiguration: .init(state: .enabled, action: { - isPresented = false - })) - .posHeaderBackButtonIcon(systemName: "xmark") + ZStack { + VStack { + POSPageHeaderView( + title: POSOrderListView.Localization.ordersTitle, + backButtonConfiguration: .init(state: .enabled, action: { + isPresented = false + })) + .posHeaderBackButtonIcon(systemName: "xmark") + Spacer() + } + + VStack { + Spacer() + POSListErrorView(error: error) { + Task { @MainActor in + await orderListModel.ordersController.loadOrders() + } + } + Spacer() + } + } + } + + @ViewBuilder + private func emptyView() -> some View { + ZStack { + VStack { + POSPageHeaderView( + title: POSOrderListView.Localization.ordersTitle, + backButtonConfiguration: .init(state: .enabled, action: { + isPresented = false + })) + .posHeaderBackButtonIcon(systemName: "xmark") + Spacer() + } - POSListErrorView(error: error) { - Task { @MainActor in - await orderListModel.ordersController.loadOrders() + VStack { + Spacer() + POSListEmptyView( + viewModel: POSOrderListEmptyViewModel(isSearching: false) + ) { + Task { @MainActor in + await orderListModel.ordersController.loadOrders() + } } + Spacer() } } } @@ -158,8 +195,18 @@ private enum Constants { } #if DEBUG -#Preview("Orders View") { +#Preview("Orders View List") { + POSOrdersView(isPresented: .constant(true)) + .environment(POSPreviewHelpers.makePreviewOrdersModel(state: POSPreviewHelpers.loadedState())) +} + +#Preview("Orders View Empty") { POSOrdersView(isPresented: .constant(true)) .environment(POSPreviewHelpers.makePreviewOrdersModel(state: .empty)) } + +#Preview("Orders View Error") { + POSOrdersView(isPresented: .constant(true)) + .environment(POSPreviewHelpers.makePreviewOrdersModel(state: .error(.errorOnLoadingOrders()))) +} #endif diff --git a/Modules/Sources/PointOfSale/Presentation/PointOfSaleAssets.swift b/Modules/Sources/PointOfSale/Presentation/PointOfSaleAssets.swift index 16aea632ea9..86560c6f231 100644 --- a/Modules/Sources/PointOfSale/Presentation/PointOfSaleAssets.swift +++ b/Modules/Sources/PointOfSale/Presentation/PointOfSaleAssets.swift @@ -20,6 +20,7 @@ enum PointOfSaleAssets: CaseIterable { case netum1228BCHIDBarcode case netum1228BCPairBarcode case testEan13Barcode + case noOrders var image: Image { Image(imageName, bundle: .module) @@ -67,6 +68,8 @@ enum PointOfSaleAssets: CaseIterable { "netum-1228bc-hid-barcode" case .netum1228BCPairBarcode: "netum-1228bc-pair-barcode" + case .noOrders: + "pos-no-orders" } } } diff --git a/Modules/Sources/PointOfSale/Presentation/Reusable Views/POSListEmptyView.swift b/Modules/Sources/PointOfSale/Presentation/Reusable Views/POSListEmptyView.swift index 496606da323..998df352377 100644 --- a/Modules/Sources/PointOfSale/Presentation/Reusable Views/POSListEmptyView.swift +++ b/Modules/Sources/PointOfSale/Presentation/Reusable Views/POSListEmptyView.swift @@ -68,7 +68,7 @@ struct POSListEmptyView: View { }, label: { Text(buttonTitle) }) - .buttonStyle(POSOutlinedButtonStyle(size: .normal)) + .buttonStyle(POSFilledButtonStyle(size: .normal)) .frame(width: viewWidth / 2) .padding([.leading, .trailing]) } diff --git a/Modules/Sources/PointOfSale/Resources/Images.xcassets/pos-no-orders.imageset/Contents.json b/Modules/Sources/PointOfSale/Resources/Images.xcassets/pos-no-orders.imageset/Contents.json new file mode 100644 index 00000000000..4f211fffaff --- /dev/null +++ b/Modules/Sources/PointOfSale/Resources/Images.xcassets/pos-no-orders.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "pos-no-orders.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Modules/Sources/PointOfSale/Resources/Images.xcassets/pos-no-orders.imageset/pos-no-orders.pdf b/Modules/Sources/PointOfSale/Resources/Images.xcassets/pos-no-orders.imageset/pos-no-orders.pdf new file mode 100644 index 00000000000..89c2f301f72 Binary files /dev/null and b/Modules/Sources/PointOfSale/Resources/Images.xcassets/pos-no-orders.imageset/pos-no-orders.pdf differ