Skip to content

Commit 74b52ab

Browse files
[Shipping Labels] Show custom error when no label rates available (#15322)
2 parents 6499a6c + a80fff1 commit 74b52ab

File tree

3 files changed

+75
-21
lines changed

3 files changed

+75
-21
lines changed

WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooShippingServiceView.swift

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ struct WooShippingServiceView: View {
3333
MissingDataStateView(title: Localization.noWeightTitle,
3434
message: Localization.noWeightMessage)
3535
case WooShippingServiceViewModel.Error.failedLoadingLabelRates:
36-
errorState
36+
ErrorState(message: Localization.failedLoadingDataError) {
37+
if let package = viewModel.selectedPackage {
38+
viewModel.loadLabelRates(for: package)
39+
}
40+
}
41+
case .noRatesAvailable:
42+
ErrorState(message: Localization.noRatesAvailable)
3743
}
3844
}
3945
}
@@ -78,35 +84,49 @@ struct WooShippingServiceView: View {
7884
.padding(.horizontal, Layout.padding * -1) // Offset the additional padding in TopTabView
7985
}
8086

81-
var errorState: some View {
82-
VStack(spacing: Layout.padding) {
87+
var progressView: some View {
88+
VStack(spacing: Layout.placeholderPadding) {
89+
Image(uiImage: .wooShippingRatesPlaceholder)
90+
91+
ProgressView()
92+
.progressViewStyle(.circular)
93+
}
94+
.frame(maxWidth: .infinity, alignment: .center)
95+
.padding(Layout.placeholderPadding)
96+
.roundedBorder(cornerRadius: 8, lineColor: Color(.border), lineWidth: 1, dashed: true)
97+
.padding(.vertical, Layout.padding)
98+
}
99+
}
100+
101+
private struct ErrorState: View {
102+
let message: String
103+
let retryAction: (() -> Void)?
104+
105+
init(message: String, retryAction: (() -> Void)? = nil) {
106+
self.message = message
107+
self.retryAction = retryAction
108+
}
109+
110+
var body: some View {
111+
VStack(spacing: WooShippingServiceView.Layout.padding) {
83112
Image(uiImage: .grayErrorIcon)
84113
.resizable()
85114
.aspectRatio(contentMode: .fit)
86115
.frame(width: Layout.errorIconSize, height: Layout.errorIconSize)
87-
Text(Localization.failedLoadingDataError)
116+
Text(message)
88117
.multilineTextAlignment(.center)
89-
Button(Localization.retryCTA) {
90-
if let package = viewModel.selectedPackage {
91-
viewModel.loadLabelRates(for: package)
118+
if let retryAction {
119+
Button(WooShippingServiceView.Localization.retryCTA) {
120+
retryAction()
92121
}
93122
}
94123
}
95124
.frame(maxWidth: .infinity, alignment: .center)
96125
.padding(WooShippingServiceView.Layout.placeholderPadding)
97126
}
98127

99-
var progressView: some View {
100-
VStack(spacing: Layout.placeholderPadding) {
101-
Image(uiImage: .wooShippingRatesPlaceholder)
102-
103-
ProgressView()
104-
.progressViewStyle(.circular)
105-
}
106-
.frame(maxWidth: .infinity, alignment: .center)
107-
.padding(Layout.placeholderPadding)
108-
.roundedBorder(cornerRadius: 8, lineColor: Color(.border), lineWidth: 1, dashed: true)
109-
.padding(.vertical, Layout.padding)
128+
enum Layout {
129+
static let errorIconSize: CGFloat = 86
110130
}
111131
}
112132

@@ -153,7 +173,6 @@ fileprivate extension WooShippingServiceView {
153173
static let padding: CGFloat = 16
154174
static let innerSpacing: CGFloat = 8
155175
static let placeholderPadding: CGFloat = 32
156-
static let errorIconSize: CGFloat = 86
157176
}
158177
}
159178

@@ -187,6 +206,11 @@ private extension WooShippingServiceView {
187206
value: "We are unable to load shipping rates",
188207
comment: "Error message when loading shipping label rates "
189208
+ "failed on the shipping label creation screen")
209+
static let noRatesAvailable = NSLocalizedString("wooShipping.createLabels.rates.noRatesAvailable",
210+
value: "We couldn't find a shipping service for the combination of the selected package "
211+
+ "and the total shipment weight. Please adjust your input and try again.",
212+
comment: "Error message when no shipping rates were found "
213+
+ "based on the combination of the selected package and the total shipment weight.")
190214
static let retryCTA = NSLocalizedString("wooShipping.createLabels.rates.retryCTA",
191215
value: "Retry",
192216
comment: "Button to retry loading data on the shipping label creation screen")

WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooShippingServiceViewModel.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,13 @@ final class WooShippingServiceViewModel: ObservableObject {
9494

9595
switch result {
9696
case let .success(rates):
97-
guard let rates = rates.first(where: { $0.packageID == selectedPackage.id }) else {
97+
guard let rates = rates.first(where: { $0.packageID == selectedPackage.id }),
98+
rates.defaultRates.isNotEmpty else {
9899
DDLogError("⛔️ Fetched shipping label rates for Woo Shipping do not include rates for selected package: \(selectedPackage)")
99-
updateLoadingState(to: .error(Error.failedLoadingLabelRates))
100+
updateLoadingState(to: .error(Error.noRatesAvailable))
100101
return
101102
}
103+
102104
standardRates = rates.defaultRates
103105
signatureRates = rates.signatureRequired
104106
adultSignatureRates = rates.adultSignatureRequired
@@ -146,6 +148,7 @@ extension WooShippingServiceViewModel {
146148
case missingDestinationAddress
147149
case missingShipmentWeight
148150
case failedLoadingLabelRates
151+
case noRatesAvailable
149152
}
150153
}
151154

WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooShippingServiceViewModelTests.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,33 @@ final class WooShippingServiceViewModelTests: XCTestCase {
261261
// Then
262262
XCTAssertEqual(viewModel.loadingState, .error(.missingShipmentWeight))
263263
}
264+
265+
func test_when_loadLabelRates_receives_empty_rates_it_sets_error_state() {
266+
// Given
267+
let stores = MockStoresManager(sessionManager: .testingInstance)
268+
let emptyRates = [ShippingLabelCarriersAndRates(packageID: Self.samplePackageID,
269+
defaultRates: [],
270+
signatureRequired: [],
271+
adultSignatureRequired: [])]
272+
stores.whenReceivingAction(ofType: WooShippingAction.self) { action in
273+
switch action {
274+
case let .loadLabelRates(_, _, _, _, packages, completion):
275+
completion(packages, .success(emptyRates))
276+
default:
277+
XCTFail("Received unexpected action: \(action)")
278+
}
279+
}
280+
let viewModel = WooShippingServiceViewModel(order: Order.fake(),
281+
originAddress: WooShippingAddress.fake(),
282+
destinationAddress: sampleDestinationAddress(),
283+
stores: stores)
284+
285+
// When
286+
viewModel.loadLabelRates(for: samplePackage)
287+
288+
// Then
289+
XCTAssertEqual(viewModel.loadingState, .error(.noRatesAvailable))
290+
}
264291
}
265292

266293
private extension WooShippingServiceViewModelTests {

0 commit comments

Comments
 (0)