Skip to content

Commit 7abea8d

Browse files
authored
Merge pull request #8656 from woocommerce/issue/8644-track-profiler-answers
Store creation: send a Tracks event with profiler answers when reaching store summary screen
2 parents dc3ddc9 + 3e3db6d commit 7abea8d

12 files changed

+136
-71
lines changed

WooCommerce/Classes/Analytics/WooAnalyticsEvent+StoreCreation.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ extension WooAnalyticsEvent {
99
static let errorType = "error_type"
1010
static let flow = "flow"
1111
static let step = "step"
12+
static let category = "industry"
13+
static let categoryGroup = "industry_group"
14+
static let sellingStatus = "user_commerce_journey"
15+
static let sellingPlatforms = "ecommerce_platforms"
16+
static let countryCode = "country_code"
1217
}
1318

1419
/// Tracked when the user taps on the CTA in store picker (logged in to WPCOM) to create a store.
@@ -54,6 +59,20 @@ extension WooAnalyticsEvent {
5459
WooAnalyticsEvent(statName: .siteCreationManageStoreTapped, properties: [:])
5560
}
5661

62+
/// Tracked when completing the last profiler question during the store creation flow.
63+
static func siteCreationProfilerData(category: StoreCreationCategoryAnswer?,
64+
sellingStatus: StoreCreationSellingStatusAnswer?,
65+
countryCode: SiteAddress.CountryCode?) -> WooAnalyticsEvent {
66+
let properties = [
67+
Key.category: category?.value,
68+
Key.categoryGroup: category?.groupValue,
69+
Key.sellingStatus: sellingStatus?.sellingStatus.rawValue,
70+
Key.sellingPlatforms: sellingStatus?.sellingPlatforms?.map { $0.rawValue }.sorted().joined(separator: ","),
71+
Key.countryCode: countryCode?.rawValue
72+
].compactMapValues({ $0 })
73+
return WooAnalyticsEvent(statName: .siteCreationProfilerData, properties: properties)
74+
}
75+
5776
/// Tracked when the user taps on the CTA in login prologue (logged out) to create a store.
5877
static func loginPrologueCreateSiteTapped() -> WooAnalyticsEvent {
5978
WooAnalyticsEvent(statName: .loginPrologueCreateSiteTapped,

WooCommerce/Classes/Analytics/WooAnalyticsStat.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ public enum WooAnalyticsStat: String {
182182
case siteCreationStep = "site_creation_step"
183183
case siteCreationSitePreviewed = "site_creation_site_previewed"
184184
case siteCreationManageStoreTapped = "site_creation_store_management_opened"
185+
case siteCreationProfilerData = "site_creation_profiler_data"
185186
case loginPrologueCreateSiteTapped = "login_prologue_create_site_tapped"
186187
case signupFormLoginTapped = "signup_login_button_tapped"
187188
case signupSubmitted = "signup_submitted"

WooCommerce/Classes/Authentication/Store Creation/Profiler/Category/StoreCreationCategoryQuestionViewModel.swift

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
import Combine
22
import Foundation
33

4+
/// Necessary data from the answer of the store creation category question.
5+
struct StoreCreationCategoryAnswer: Equatable {
6+
/// Display name of the selected category.
7+
let name: String
8+
/// Raw value of the category (industry) to be sent to the backend.
9+
let value: String
10+
/// Raw value of the category group (industry group) to be sent to the backend.
11+
let groupValue: String
12+
}
13+
414
/// View model for `StoreCreationCategoryQuestionView`, an optional profiler question about store category in the store creation flow.
515
@MainActor
616
final class StoreCreationCategoryQuestionViewModel: StoreCreationProfilerQuestionViewModel, ObservableObject {
17+
typealias Answer = StoreCreationCategoryAnswer
18+
719
let topHeader: String
820

921
let title: String = Localization.title
@@ -14,11 +26,11 @@ final class StoreCreationCategoryQuestionViewModel: StoreCreationProfilerQuestio
1426
/// TODO: 8376 - update values when API is ready.
1527
@Published private(set) var selectedCategory: Category?
1628

17-
private let onContinue: (String) -> Void
29+
private let onContinue: (Answer) -> Void
1830
private let onSkip: () -> Void
1931

2032
init(storeName: String,
21-
onContinue: @escaping (String) -> Void,
33+
onContinue: @escaping (Answer) -> Void,
2234
onSkip: @escaping () -> Void) {
2335
self.topHeader = storeName
2436
self.onContinue = onContinue
@@ -28,11 +40,12 @@ final class StoreCreationCategoryQuestionViewModel: StoreCreationProfilerQuestio
2840

2941
extension StoreCreationCategoryQuestionViewModel: OptionalStoreCreationProfilerQuestionViewModel {
3042
func continueButtonTapped() async {
31-
guard let selectedCategory else {
43+
guard let selectedCategory,
44+
let categoryGroup = categorySections.first(where: { $0.categories.contains(selectedCategory) })?.group else {
3245
return onSkip()
3346
}
3447

35-
onContinue(selectedCategory.name)
48+
onContinue(.init(name: selectedCategory.name, value: selectedCategory.rawValue, groupValue: categoryGroup.rawValue))
3649
}
3750

3851
func skipButtonTapped() {

WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingPlatformsQuestionView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import SwiftUI
55
struct StoreCreationSellingPlatformsQuestionView: View {
66
@ObservedObject private var viewModel: StoreCreationSellingPlatformsQuestionViewModel
77

8-
init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
8+
init(storeName: String, onContinue: @escaping (StoreCreationSellingStatusAnswer?) -> Void, onSkip: @escaping () -> Void) {
99
self.viewModel = StoreCreationSellingPlatformsQuestionViewModel(storeName: storeName, onContinue: onContinue, onSkip: onSkip)
1010
}
1111

@@ -31,7 +31,7 @@ struct StoreCreationSellingPlatformsQuestionView: View {
3131
struct StoreCreationSellingPlatformsQuestionView_Previews: PreviewProvider {
3232
static var previews: some View {
3333
NavigationView {
34-
StoreCreationSellingPlatformsQuestionView(storeName: "New Year Store", onContinue: {}, onSkip: {})
34+
StoreCreationSellingPlatformsQuestionView(storeName: "New Year Store", onContinue: { _ in }, onSkip: {})
3535
}
3636
}
3737
}

WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingPlatformsQuestionViewModel.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import Foundation
99
final class StoreCreationSellingPlatformsQuestionViewModel: StoreCreationProfilerQuestionViewModel, ObservableObject {
1010
/// Other online platforms that the user might be selling. Source of truth:
1111
/// https://github.com/Automattic/woocommerce.com/blob/trunk/themes/woo/start/config/options.json
12-
enum Platform: Equatable, CaseIterable {
12+
enum Platform: String, CaseIterable {
1313
case amazon
14-
case bigCartel
15-
case bigCommerce
16-
case eBay
14+
case bigCartel = "big-cartel"
15+
case bigCommerce = "big-commerce"
16+
case eBay = "ebay"
1717
case etsy
18-
case facebookMarketplace
19-
case googleShopping
18+
case facebookMarketplace = "facebook-marketplace"
19+
case googleShopping = "google-shopping"
2020
case pinterest
2121
case shopify
2222
case square
@@ -37,11 +37,11 @@ final class StoreCreationSellingPlatformsQuestionViewModel: StoreCreationProfile
3737

3838
@Published private(set) var selectedPlatforms: Set<Platform> = []
3939

40-
private let onContinue: () -> Void
40+
private let onContinue: (StoreCreationSellingStatusAnswer?) -> Void
4141
private let onSkip: () -> Void
4242

4343
init(storeName: String,
44-
onContinue: @escaping () -> Void,
44+
onContinue: @escaping (StoreCreationSellingStatusAnswer?) -> Void,
4545
onSkip: @escaping () -> Void) {
4646
self.topHeader = storeName
4747
self.onContinue = onContinue
@@ -52,7 +52,7 @@ final class StoreCreationSellingPlatformsQuestionViewModel: StoreCreationProfile
5252
extension StoreCreationSellingPlatformsQuestionViewModel: OptionalStoreCreationProfilerQuestionViewModel {
5353
func continueButtonTapped() async {
5454
// TODO: submission API.
55-
onContinue()
55+
onContinue(.init(sellingStatus: .alreadySellingOnline, sellingPlatforms: selectedPlatforms))
5656
}
5757

5858
func skipButtonTapped() {

WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionContainerView.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import SwiftUI
22

3+
/// Necessary data from the answer to the store creation selling status question.
4+
struct StoreCreationSellingStatusAnswer: Equatable {
5+
/// The status of the merchant's eCommerce experience.
6+
let sellingStatus: StoreCreationSellingStatusQuestionViewModel.SellingStatus
7+
/// The eCommerce platforms that the merchant is already selling on.
8+
/// When the merchant isn't already selling online, the value is `nil`.
9+
let sellingPlatforms: Set<StoreCreationSellingPlatformsQuestionViewModel.Platform>?
10+
}
11+
312
/// Hosting controller that wraps the `StoreCreationSellingStatusQuestionContainerView`.
413
final class StoreCreationSellingStatusQuestionHostingController: UIHostingController<StoreCreationSellingStatusQuestionContainerView> {
5-
init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
14+
init(storeName: String, onContinue: @escaping (StoreCreationSellingStatusAnswer?) -> Void, onSkip: @escaping () -> Void) {
615
super.init(rootView: StoreCreationSellingStatusQuestionContainerView(storeName: storeName, onContinue: onContinue, onSkip: onSkip))
716
}
817

@@ -23,10 +32,10 @@ final class StoreCreationSellingStatusQuestionHostingController: UIHostingContro
2332
struct StoreCreationSellingStatusQuestionContainerView: View {
2433
@StateObject private var viewModel: StoreCreationSellingStatusQuestionViewModel
2534
private let storeName: String
26-
private let onContinue: () -> Void
35+
private let onContinue: (StoreCreationSellingStatusAnswer?) -> Void
2736
private let onSkip: () -> Void
2837

29-
init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
38+
init(storeName: String, onContinue: @escaping (StoreCreationSellingStatusAnswer?) -> Void, onSkip: @escaping () -> Void) {
3039
self._viewModel = StateObject(wrappedValue: StoreCreationSellingStatusQuestionViewModel(storeName: storeName, onContinue: onContinue, onSkip: onSkip))
3140
self.storeName = storeName
3241
self.onContinue = onContinue
@@ -45,7 +54,7 @@ struct StoreCreationSellingStatusQuestionContainerView: View {
4554
struct StoreCreationSellingStatusQuestionContainerView_Previews: PreviewProvider {
4655
static var previews: some View {
4756
NavigationView {
48-
StoreCreationSellingStatusQuestionContainerView(storeName: "New Year Store", onContinue: {}, onSkip: {})
57+
StoreCreationSellingStatusQuestionContainerView(storeName: "New Year Store", onContinue: { _ in }, onSkip: {})
4958
}
5059
}
5160
}

WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct StoreCreationSellingStatusQuestionView: View {
3030
struct StoreCreationSellingStatusQuestionView_Previews: PreviewProvider {
3131
static var previews: some View {
3232
NavigationView {
33-
StoreCreationSellingStatusQuestionView(viewModel: .init(storeName: "New Year Store", onContinue: {}, onSkip: {}))
33+
StoreCreationSellingStatusQuestionView(viewModel: .init(storeName: "New Year Store", onContinue: { _ in }, onSkip: {}))
3434
}
3535
}
3636
}

WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionViewModel.swift

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import Foundation
55
@MainActor
66
final class StoreCreationSellingStatusQuestionViewModel: StoreCreationProfilerQuestionViewModel, ObservableObject {
77
/// Selling status options.
8+
/// Its raw value is the value to be sent to the backend.
89
/// https://github.com/Automattic/woocommerce.com/blob/trunk/themes/woo/start/config/options.json
9-
enum SellingStatus: Equatable {
10+
enum SellingStatus: String {
1011
/// Just starting my business.
11-
case justStarting
12+
case justStarting = "im_just_starting_my_business"
1213
/// Already selling, but not online.
13-
case alreadySellingButNotOnline
14+
case alreadySellingButNotOnline = "im_already_selling_but_not_online"
1415
/// Already selling online.
15-
case alreadySellingOnline
16+
case alreadySellingOnline = "im_already_selling_online"
1617
}
1718

1819
let topHeader: String
@@ -30,11 +31,11 @@ final class StoreCreationSellingStatusQuestionViewModel: StoreCreationProfilerQu
3031
/// Set to `true` when the user selects the selling status as "I am already selling online".
3132
@Published private(set) var isAlreadySellingOnline: Bool = false
3233

33-
private let onContinue: () -> Void
34+
private let onContinue: (StoreCreationSellingStatusAnswer?) -> Void
3435
private let onSkip: () -> Void
3536

3637
init(storeName: String,
37-
onContinue: @escaping () -> Void,
38+
onContinue: @escaping (StoreCreationSellingStatusAnswer?) -> Void,
3839
onSkip: @escaping () -> Void) {
3940
self.topHeader = storeName
4041
self.onContinue = onContinue
@@ -48,15 +49,15 @@ final class StoreCreationSellingStatusQuestionViewModel: StoreCreationProfilerQu
4849

4950
extension StoreCreationSellingStatusQuestionViewModel: OptionalStoreCreationProfilerQuestionViewModel {
5051
func continueButtonTapped() async {
51-
guard selectedStatus != nil else {
52+
guard let selectedStatus else {
5253
return onSkip()
5354
}
5455
guard selectedStatus != .alreadySellingOnline else {
5556
// Handled in `StoreCreationSellingPlatformsQuestionViewModel`.
5657
return
5758
}
5859
// TODO: submission API.
59-
onContinue()
60+
onContinue(.init(sellingStatus: selectedStatus, sellingPlatforms: nil))
6061
}
6162

6263
func skipButtonTapped() {

0 commit comments

Comments
 (0)