diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubView.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubView.swift index d3af142266e..b7828f3a5a4 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubView.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubView.swift @@ -36,10 +36,16 @@ struct AnalyticsHubView: View { VStack(spacing: 0) { Divider() - Text("Placeholder For Revenue Card") - .padding(.leading) - .frame(maxWidth: .infinity, minHeight: 220, alignment: .leading) - .background(Color(uiColor: .listForeground)) + AnalyticsReportCard(title: "REVENUE", + leadingTitle: "Total Sales", + leadingValue: "$3.234", + leadingDelta: "+23%", + leadingDeltaColor: .withColorStudio(.green, shade: .shade50), + trailingTitle: "Net Sales", + trailingValue: "$2.324", + trailingDelta: "-4%", + trailingDeltaColor: .withColorStudio(.red, shade: .shade40)) + .background(Color(uiColor: .listForeground)) Divider() } @@ -47,10 +53,16 @@ struct AnalyticsHubView: View { VStack(spacing: 0) { Divider() - Text("Placeholder For Orders Card") - .padding(.leading) - .frame(maxWidth: .infinity, minHeight: 220, alignment: .leading) - .background(Color(uiColor: .listForeground)) + AnalyticsReportCard(title: "ORDERS", + leadingTitle: "Total Orders", + leadingValue: "145", + leadingDelta: "+36%", + leadingDeltaColor: .withColorStudio(.green, shade: .shade50), + trailingTitle: "Average Order Value", + trailingValue: "$57,99", + trailingDelta: "-16%", + trailingDeltaColor: .withColorStudio(.red, shade: .shade40)) + .background(Color(uiColor: .listForeground)) Divider() } diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/StatsCard.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/StatsCard.swift new file mode 100644 index 00000000000..a617f0d32e9 --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/StatsCard.swift @@ -0,0 +1,99 @@ +import SwiftUI + +/// Resuable report card made for the Analytics Hub. +/// +struct AnalyticsReportCard: View { + + let title: String + let leadingTitle: String + let leadingValue: String + let leadingDelta: String + let leadingDeltaColor: UIColor + let trailingTitle: String + let trailingValue: String + let trailingDelta: String + let trailingDeltaColor: UIColor + + var body: some View { + VStack(alignment: .leading, spacing: Layout.titleSpacing) { + + Text(title) + .foregroundColor(Color(.text)) + .footnoteStyle() + + HStack { + + /// Leading Column + /// + VStack(alignment: .leading, spacing: Layout.columnSpacing) { + + Text(leadingTitle) + .calloutStyle() + + Text(leadingValue) + .titleStyle() + + DeltaTag(value: leadingDelta, backgroundColor: leadingDeltaColor) + + } + .frame(maxWidth: .infinity, alignment: .leading) + + /// Trailing Column + /// + VStack(alignment: .leading, spacing: Layout.columnSpacing) { + Text(trailingTitle) + .calloutStyle() + + Text(trailingValue) + .titleStyle() + + DeltaTag(value: trailingDelta, backgroundColor: trailingDeltaColor) + } + .frame(maxWidth: .infinity, alignment: .leading) + } + } + .padding(Layout.cardPadding) + } +} + +private struct DeltaTag: View { + + let value: String + let backgroundColor: UIColor + + var body: some View { + Text(value) + .padding(AnalyticsReportCard.Layout.deltaBackgroundPadding) + .foregroundColor(Color(.textInverted)) + .captionStyle() + .background(Color(backgroundColor)) + .cornerRadius(AnalyticsReportCard.Layout.deltaCornerRadius) + } +} + +// MARK: Constants +private extension AnalyticsReportCard { + enum Layout { + static let titleSpacing: CGFloat = 24 + static let cardPadding: CGFloat = 16 + static let columnSpacing: CGFloat = 10 + static let deltaBackgroundPadding = EdgeInsets(top: 2, leading: 8, bottom: 2, trailing: 8) + static let deltaCornerRadius: CGFloat = 4.0 + } +} + +// MARK: Previews +struct Previews: PreviewProvider { + static var previews: some View { + AnalyticsReportCard(title: "REVENUE", + leadingTitle: "Total Sales", + leadingValue: "$3.678", + leadingDelta: "+23%", + leadingDeltaColor: .withColorStudio(.green, shade: .shade40), + trailingTitle: "Net Sales", + trailingValue: "$3.232", + trailingDelta: "-3%", + trailingDeltaColor: .withColorStudio(.red, shade: .shade40)) + .previewLayout(.sizeThatFits) + } +} diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 17227d93784..142ac632168 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -559,6 +559,7 @@ 263E38472641FF3400260D3B /* Codegen in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 263E38452641FF3400260D3B /* Codegen */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 263EB409242C58EA00F3A15F /* ProductFormActionsFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 263EB408242C58EA00F3A15F /* ProductFormActionsFactoryTests.swift */; }; 2647F7B529280A7F00D59FDF /* AnalyticsHubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2647F7B429280A7F00D59FDF /* AnalyticsHubView.swift */; }; + 2647F7BA292BE2F900D59FDF /* StatsCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2647F7B9292BE2F900D59FDF /* StatsCard.swift */; }; 265284022624937600F91BA1 /* AddOnCrossreferenceUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 265284012624937600F91BA1 /* AddOnCrossreferenceUseCase.swift */; }; 265284092624ACE900F91BA1 /* AddOnCrossreferenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 265284082624ACE900F91BA1 /* AddOnCrossreferenceTests.swift */; }; 2655905B27863D1300BB8457 /* MockCollectOrderPaymentUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2655905A27863D1300BB8457 /* MockCollectOrderPaymentUseCase.swift */; }; @@ -2522,6 +2523,7 @@ 26309F16277D0AEA0012797F /* SafeAreaInsetsKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafeAreaInsetsKey.swift; sourceTree = ""; }; 263EB408242C58EA00F3A15F /* ProductFormActionsFactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductFormActionsFactoryTests.swift; sourceTree = ""; }; 2647F7B429280A7F00D59FDF /* AnalyticsHubView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsHubView.swift; sourceTree = ""; }; + 2647F7B9292BE2F900D59FDF /* StatsCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsCard.swift; sourceTree = ""; }; 265284012624937600F91BA1 /* AddOnCrossreferenceUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOnCrossreferenceUseCase.swift; sourceTree = ""; }; 265284082624ACE900F91BA1 /* AddOnCrossreferenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOnCrossreferenceTests.swift; sourceTree = ""; }; 2655905A27863D1300BB8457 /* MockCollectOrderPaymentUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCollectOrderPaymentUseCase.swift; sourceTree = ""; }; @@ -5237,6 +5239,7 @@ isa = PBXGroup; children = ( 2647F7B429280A7F00D59FDF /* AnalyticsHubView.swift */, + 2647F7B9292BE2F900D59FDF /* StatsCard.swift */, ); path = "Analytics Hub"; sourceTree = ""; @@ -10170,6 +10173,7 @@ 02482A8B237BE8C7007E73ED /* LinkSettingsViewController.swift in Sources */, CE227097228F152400C0626C /* WooBasicTableViewCell.swift in Sources */, 02C27BCE282CB52F0065471A /* CardPresentPaymentReceiptEmailCoordinator.swift in Sources */, + 2647F7BA292BE2F900D59FDF /* StatsCard.swift in Sources */, 451526392577D89E0076B03C /* AddAttributeViewModel.swift in Sources */, DE0A2EAF281BA278007A8015 /* ProductCategorySelectorViewModel.swift in Sources */, 45B9C64323A91CB6007FC4C5 /* PriceInputFormatter.swift in Sources */, diff --git a/WooFoundation/WooFoundation/ViewModifiers/WooStyleModifiers.swift b/WooFoundation/WooFoundation/ViewModifiers/WooStyleModifiers.swift index 337fbd0f3c5..9eab64041eb 100644 --- a/WooFoundation/WooFoundation/ViewModifiers/WooStyleModifiers.swift +++ b/WooFoundation/WooFoundation/ViewModifiers/WooStyleModifiers.swift @@ -104,6 +104,22 @@ public struct FootnoteStyle: ViewModifier { } } +public struct CalloutStyle: ViewModifier { + public func body(content: Content) -> some View { + content + .font(.callout) + .foregroundColor(Color(.textSubtle)) + } +} + +public struct CaptionStyle: ViewModifier { + public func body(content: Content) -> some View { + content + .font(.caption) + .foregroundColor(Color(.text)) + } +} + public struct ErrorStyle: ViewModifier { public func body(content: Content) -> some View { content @@ -201,4 +217,12 @@ public extension View { func headlineLinkStyle() -> some View { self.modifier(HeadlineLinkStyle()) } + + func calloutStyle() -> some View { + self.modifier(CalloutStyle()) + } + + func captionStyle() -> some View { + self.modifier(CaptionStyle()) + } }