Skip to content

Commit c2b646e

Browse files
committed
Show sync error below analytics card placeholders
1 parent f8cbf03 commit c2b646e

File tree

7 files changed

+116
-72
lines changed

7 files changed

+116
-72
lines changed

WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubView.swift

Lines changed: 9 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -41,41 +41,29 @@ struct AnalyticsHubView: View {
4141
VStack(spacing: Layout.dividerSpacing) {
4242
Divider()
4343

44-
if let viewModel = viewModel.revenueCard {
45-
AnalyticsReportCard(viewModel: viewModel)
46-
.padding(.horizontal, insets: safeAreaInsets)
47-
.background(Color(uiColor: .listForeground))
48-
} else {
49-
emptyCard(message: Localization.noRevenue)
50-
}
44+
AnalyticsReportCard(viewModel: viewModel.revenueCard)
45+
.padding(.horizontal, insets: safeAreaInsets)
46+
.background(Color(uiColor: .listForeground))
5147

5248
Divider()
5349
}
5450

5551
VStack(spacing: Layout.dividerSpacing) {
5652
Divider()
5753

58-
if let viewModel = viewModel.ordersCard {
59-
AnalyticsReportCard(viewModel: viewModel)
60-
.padding(.horizontal, insets: safeAreaInsets)
61-
.background(Color(uiColor: .listForeground))
62-
} else {
63-
emptyCard(message: Localization.noOrders)
64-
}
54+
AnalyticsReportCard(viewModel: viewModel.ordersCard)
55+
.padding(.horizontal, insets: safeAreaInsets)
56+
.background(Color(uiColor: .listForeground))
6557

6658
Divider()
6759
}
6860

6961
VStack(spacing: Layout.dividerSpacing) {
7062
Divider()
7163

72-
if let viewModel = viewModel.productCard {
73-
AnalyticsProductCard(viewModel: viewModel)
74-
.padding(.horizontal, insets: safeAreaInsets)
75-
.background(Color(uiColor: .listForeground))
76-
} else {
77-
emptyCard(message: Localization.noProducts)
78-
}
64+
AnalyticsProductCard(viewModel: viewModel.productCard)
65+
.padding(.horizontal, insets: safeAreaInsets)
66+
.background(Color(uiColor: .listForeground))
7967

8068
Divider()
8169
}
@@ -91,29 +79,13 @@ struct AnalyticsHubView: View {
9179
await viewModel.updateData()
9280
}
9381
}
94-
95-
@ViewBuilder
96-
private func emptyCard(message: String) -> some View {
97-
Text(message)
98-
.frame(maxWidth: .infinity)
99-
.foregroundColor(Color(.text))
100-
.subheadlineStyle()
101-
.padding()
102-
.background(Color(uiColor: .listForeground))
103-
}
10482
}
10583

10684
/// Constants
10785
///
10886
private extension AnalyticsHubView {
10987
struct Localization {
11088
static let title = NSLocalizedString("Analytics", comment: "Title for the Analytics Hub screen.")
111-
static let noRevenue = NSLocalizedString("Unable to load revenue analytics",
112-
comment: "Text displayed when there is an error loading revenue stats data.")
113-
static let noOrders = NSLocalizedString("Unable to load order analytics",
114-
comment: "Text displayed when there is an error loading order stats data.")
115-
static let noProducts = NSLocalizedString("Unable to load product analytics",
116-
comment: "Text displayed when there is an error loading product stats data.")
11789
}
11890

11991
struct Layout {

WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubViewModel.swift

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,19 @@ final class AnalyticsHubViewModel: ObservableObject {
2828

2929
/// Revenue Card ViewModel
3030
///
31-
/// Setting an `AnalyticsHubViewModel` with `nil` order data displays the revenue card with placeholders.
32-
/// Setting the view model to `nil` displays an error message instead of the revenue card.
33-
///
34-
@Published var revenueCard: AnalyticsReportCardViewModel? = AnalyticsHubViewModel.revenueCard(currentPeriodStats: nil, previousPeriodStats: nil)
31+
@Published var revenueCard = AnalyticsHubViewModel.revenueCard(currentPeriodStats: nil, previousPeriodStats: nil)
3532

3633
/// Orders Card ViewModel
3734
///
38-
/// Setting an `AnalyticsHubViewModel` with `nil` order data displays the order card with placeholders.
39-
/// Setting the view model to `nil` displays an error message instead of the order card.
40-
///
41-
@Published var ordersCard: AnalyticsReportCardViewModel? = AnalyticsHubViewModel.ordersCard(currentPeriodStats: nil, previousPeriodStats: nil)
35+
@Published var ordersCard = AnalyticsHubViewModel.ordersCard(currentPeriodStats: nil, previousPeriodStats: nil)
4236

4337
/// Time Range ViewModel
4438
///
4539
@Published var timeRangeCard: AnalyticsTimeRangeCardViewModel
4640

4741
/// Products Card ViewModel
4842
///
49-
/// Setting an `AnalyticsProductCardViewModel` with `nil` order data displays the product card with placeholders.
50-
/// Setting the view model to `nil` displays an error message instead of the product card.
51-
///
52-
@Published var productCard: AnalyticsProductCardViewModel? = AnalyticsHubViewModel.productCard(currentPeriodStats: nil, previousPeriodStats: nil)
43+
@Published var productCard = AnalyticsHubViewModel.productCard(currentPeriodStats: nil, previousPeriodStats: nil)
5344

5445
// MARK: Private data
5546

@@ -120,15 +111,14 @@ private extension AnalyticsHubViewModel {
120111
private extension AnalyticsHubViewModel {
121112

122113
func switchToLoadingState() {
123-
self.revenueCard = revenueCard?.redacted
124-
self.ordersCard = ordersCard?.redacted
125-
self.productCard = productCard?.redacted
114+
self.revenueCard = revenueCard.redacted
115+
self.ordersCard = ordersCard.redacted
116+
self.productCard = productCard.redacted
126117
}
127118

128119
func switchToErrorState() {
129-
self.revenueCard = nil
130-
self.ordersCard = nil
131-
self.productCard = nil
120+
self.currentOrderStats = nil
121+
self.previousOrderStats = nil
132122
}
133123

134124
func bindViewModelsWithData() {
@@ -144,6 +134,7 @@ private extension AnalyticsHubViewModel {
144134
}
145135

146136
static func revenueCard(currentPeriodStats: OrderStatsV4?, previousPeriodStats: OrderStatsV4?) -> AnalyticsReportCardViewModel {
137+
let showSyncError = currentPeriodStats == nil || previousPeriodStats == nil
147138
let totalDelta = StatsDataTextFormatter.createTotalRevenueDelta(from: previousPeriodStats, to: currentPeriodStats)
148139
let netDelta = StatsDataTextFormatter.createNetRevenueDelta(from: previousPeriodStats, to: currentPeriodStats)
149140

@@ -159,10 +150,13 @@ private extension AnalyticsHubViewModel {
159150
trailingDelta: netDelta.string,
160151
trailingDeltaColor: Constants.deltaColor(for: netDelta.direction),
161152
trailingChartData: StatsIntervalDataParser.getChartData(for: .netRevenue, from: currentPeriodStats),
162-
isRedacted: false)
153+
isRedacted: false,
154+
showSyncError: showSyncError,
155+
syncErrorMessage: Localization.RevenueCard.noRevenue)
163156
}
164157

165158
static func ordersCard(currentPeriodStats: OrderStatsV4?, previousPeriodStats: OrderStatsV4?) -> AnalyticsReportCardViewModel {
159+
let showSyncError = currentPeriodStats == nil || previousPeriodStats == nil
166160
let ordersCountDelta = StatsDataTextFormatter.createOrderCountDelta(from: previousPeriodStats, to: currentPeriodStats)
167161
let orderValueDelta = StatsDataTextFormatter.createAverageOrderValueDelta(from: previousPeriodStats, to: currentPeriodStats)
168162

@@ -178,17 +172,21 @@ private extension AnalyticsHubViewModel {
178172
trailingDelta: orderValueDelta.string,
179173
trailingDeltaColor: Constants.deltaColor(for: orderValueDelta.direction),
180174
trailingChartData: StatsIntervalDataParser.getChartData(for: .averageOrderValue, from: currentPeriodStats),
181-
isRedacted: false)
175+
isRedacted: false,
176+
showSyncError: showSyncError,
177+
syncErrorMessage: Localization.OrderCard.noOrders)
182178
}
183179

184180
static func productCard(currentPeriodStats: OrderStatsV4?, previousPeriodStats: OrderStatsV4?) -> AnalyticsProductCardViewModel {
181+
let showSyncError = currentPeriodStats == nil || previousPeriodStats == nil
185182
let itemsSold = StatsDataTextFormatter.createItemsSoldText(orderStats: currentPeriodStats)
186183
let itemsSoldDelta = StatsDataTextFormatter.createOrderItemsSoldDelta(from: previousPeriodStats, to: currentPeriodStats)
187184

188185
return AnalyticsProductCardViewModel(itemsSold: itemsSold,
189186
delta: itemsSoldDelta.string,
190187
deltaBackgroundColor: Constants.deltaColor(for: itemsSoldDelta.direction),
191-
isRedacted: false)
188+
isRedacted: false,
189+
showSyncError: showSyncError)
192190
}
193191
}
194192

@@ -210,12 +208,16 @@ private extension AnalyticsHubViewModel {
210208
static let title = NSLocalizedString("REVENUE", comment: "Title for revenue analytics section in the Analytics Hub")
211209
static let leadingTitle = NSLocalizedString("Total Sales", comment: "Label for total sales (gross revenue) in the Analytics Hub")
212210
static let trailingTitle = NSLocalizedString("Net Sales", comment: "Label for net sales (net revenue) in the Analytics Hub")
211+
static let noRevenue = NSLocalizedString("Unable to load revenue analytics",
212+
comment: "Text displayed when there is an error loading revenue stats data.")
213213
}
214214

215215
enum OrderCard {
216216
static let title = NSLocalizedString("ORDERS", comment: "Title for order analytics section in the Analytics Hub")
217217
static let leadingTitle = NSLocalizedString("Total Orders", comment: "Label for total number of orders in the Analytics Hub")
218218
static let trailingTitle = NSLocalizedString("Average Order Value", comment: "Label for average value of orders in the Analytics Hub")
219+
static let noOrders = NSLocalizedString("Unable to load order analytics",
220+
comment: "Text displayed when there is an error loading order stats data.")
219221
}
220222
}
221223
}

WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsProductCard.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ struct AnalyticsProductCard: View {
1818
///
1919
let isRedacted: Bool
2020

21+
/// Indicates if there was an error loading the data for the card
22+
///
23+
let showSyncError: Bool
24+
2125
var body: some View {
2226
VStack(alignment: .leading) {
2327

@@ -41,6 +45,14 @@ struct AnalyticsProductCard: View {
4145
.redacted(reason: isRedacted ? .placeholder : [])
4246
.shimmering(active: isRedacted)
4347
}
48+
49+
if showSyncError {
50+
Text(Localization.noProducts)
51+
.foregroundColor(Color(.text))
52+
.subheadlineStyle()
53+
.frame(maxWidth: .infinity, alignment: .leading)
54+
.padding(.top, Layout.columnSpacing)
55+
}
4456
}
4557
.padding(Layout.cardPadding)
4658
}
@@ -51,6 +63,8 @@ private extension AnalyticsProductCard {
5163
enum Localization {
5264
static let title = NSLocalizedString("Products", comment: "Title for the products card on the analytics hub screen.").localizedUppercase
5365
static let itemsSold = NSLocalizedString("Items Sold", comment: "Title for the items sold column on the products card on the analytics hub screen.")
66+
static let noProducts = NSLocalizedString("Unable to load product analytics",
67+
comment: "Text displayed when there is an error loading product stats data.")
5468
}
5569

5670
enum Layout {
@@ -67,7 +81,16 @@ struct AnalyticsProductCardPreviews: PreviewProvider {
6781
AnalyticsProductCard(itemsSold: "2,234",
6882
delta: "+23%",
6983
deltaBackgroundColor: .withColorStudio(.green, shade: .shade50),
70-
isRedacted: false)
84+
isRedacted: false,
85+
showSyncError: false)
86+
.previewLayout(.sizeThatFits)
87+
88+
AnalyticsProductCard(itemsSold: "-",
89+
delta: "0%",
90+
deltaBackgroundColor: .withColorStudio(.gray, shade: .shade0),
91+
isRedacted: false,
92+
showSyncError: true)
7193
.previewLayout(.sizeThatFits)
94+
.previewDisplayName("No data")
7295
}
7396
}

WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsProductCardViewModel.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ struct AnalyticsProductCardViewModel {
2020
/// Indicates if the values should be hidden (for loading state)
2121
///
2222
let isRedacted: Bool
23+
24+
/// Indicates if there was an error loading the data for the card
25+
///
26+
let showSyncError: Bool
2327
}
2428

2529
extension AnalyticsProductCardViewModel {
@@ -31,7 +35,8 @@ extension AnalyticsProductCardViewModel {
3135
.init(itemsSold: "1000",
3236
delta: "+50%",
3337
deltaBackgroundColor: .lightGray,
34-
isRedacted: true)
38+
isRedacted: true,
39+
showSyncError: false)
3540
}
3641
}
3742

@@ -43,5 +48,6 @@ extension AnalyticsProductCard {
4348
self.delta = viewModel.delta
4449
self.deltaBackgroundColor = viewModel.deltaBackgroundColor
4550
self.isRedacted = viewModel.isRedacted
51+
self.showSyncError = viewModel.showSyncError
4652
}
4753
}

WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsReportCard.swift

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ struct AnalyticsReportCard: View {
1818

1919
let isRedacted: Bool
2020

21+
let showSyncError: Bool
22+
let syncErrorMessage: String
23+
2124
// Layout metrics that scale based on accessibility changes
2225
@ScaledMetric private var scaledChartWidth: CGFloat = Layout.chartWidth
2326
@ScaledMetric private var scaledChartHeight: CGFloat = Layout.chartHeight
@@ -79,6 +82,13 @@ struct AnalyticsReportCard: View {
7982
}
8083
.frame(maxWidth: .infinity, alignment: .leading)
8184
}
85+
86+
if showSyncError {
87+
Text(syncErrorMessage)
88+
.foregroundColor(Color(.text))
89+
.subheadlineStyle()
90+
.frame(maxWidth: .infinity, alignment: .leading)
91+
}
8292
}
8393
.padding(Layout.cardPadding)
8494
}
@@ -110,7 +120,26 @@ struct Previews: PreviewProvider {
110120
trailingDelta: "-3%",
111121
trailingDeltaColor: .withColorStudio(.red, shade: .shade40),
112122
trailingChartData: [50.0, 15.0, 20.0, 2.0, 10.0, 0.0, 40.0, 15.0, 20.0, 2.0, 10.0, 0.0],
113-
isRedacted: false)
123+
isRedacted: false,
124+
showSyncError: false,
125+
syncErrorMessage: "")
126+
.previewLayout(.sizeThatFits)
127+
128+
AnalyticsReportCard(title: "REVENUE",
129+
leadingTitle: "Total Sales",
130+
leadingValue: "-",
131+
leadingDelta: "0%",
132+
leadingDeltaColor: .withColorStudio(.gray, shade: .shade0),
133+
leadingChartData: [],
134+
trailingTitle: "Net Sales",
135+
trailingValue: "-",
136+
trailingDelta: "0%",
137+
trailingDeltaColor: .withColorStudio(.gray, shade: .shade0),
138+
trailingChartData: [],
139+
isRedacted: false,
140+
showSyncError: true,
141+
syncErrorMessage: "Error loading revenue analytics")
114142
.previewLayout(.sizeThatFits)
143+
.previewDisplayName("No data")
115144
}
116145
}

WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsReportCardViewModel.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ struct AnalyticsReportCardViewModel {
5252
/// Indicates if the values should be hidden (for loading state)
5353
///
5454
let isRedacted: Bool
55+
56+
/// Indicates if there was an error loading the data for the card
57+
///
58+
let showSyncError: Bool
59+
60+
/// Message to display if there was an error loading the data for the card
61+
///
62+
let syncErrorMessage: String
5563
}
5664

5765
extension AnalyticsReportCardViewModel {
@@ -71,7 +79,9 @@ extension AnalyticsReportCardViewModel {
7179
trailingDelta: "+50%",
7280
trailingDeltaColor: .lightGray,
7381
trailingChartData: [],
74-
isRedacted: true)
82+
isRedacted: true,
83+
showSyncError: false,
84+
syncErrorMessage: "")
7585
}
7686
}
7787

@@ -91,5 +101,7 @@ extension AnalyticsReportCard {
91101
self.trailingDeltaColor = viewModel.trailingDeltaColor
92102
self.trailingChartData = viewModel.trailingChartData
93103
self.isRedacted = viewModel.isRedacted
104+
self.showSyncError = viewModel.showSyncError
105+
self.syncErrorMessage = viewModel.syncErrorMessage
94106
}
95107
}

0 commit comments

Comments
 (0)