Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Woo POS] Coupons: Creation (with dummy entry point UI) #15467

Merged
merged 10 commits into from
Apr 3, 2025
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import SwiftUI
import struct Yosemite.Coupon
import enum Yosemite.POSItem
import struct Yosemite.POSCoupon

extension View {
func posCouponCreationSheet(
isPresented: Binding<Bool>,
onSuccess: @escaping (POSItem) -> Void
) -> some View {
modifier(POSCouponCreationSheetModifier(isPresented: isPresented, onSuccess: onSuccess))
}
}

private struct POSCouponCreationSheetModifier: ViewModifier {
@Binding var isPresented: Bool
let onSuccess: (POSItem) -> Void

@State private var selectedType: POSCouponDiscountType?
@State private var showCouponSelectionSheet: Bool = false

func body(content: Content) -> some View {
content
.sheet(item: $selectedType) { (posDiscountType: POSCouponDiscountType) in
var addedCouponItem: POSItem?
let viewModel = AddEditCouponViewModel(discountType: posDiscountType.discountType, onSuccess: { coupon in
addedCouponItem = .coupon(.init(id: UUID(), code: coupon.code))
})
var view = AddEditCoupon(viewModel)

view.dismissHandler = {
selectedType = nil
}

view.onDisappear = {
if let couponItem = addedCouponItem {
selectedType = nil
onSuccess(couponItem)
addedCouponItem = nil
}
}

view.discountTypeHandler = { _ in
showCouponSelectionSheet = true
}

return view
.interactiveDismissDisabled()
.discountTypeSelectionSheet(isPresented: $showCouponSelectionSheet) { type in
showCouponSelectionSheet = false
viewModel.discountType = type.discountType
}
}
.discountTypeSelectionSheet(isPresented: $isPresented) { type in
selectedType = type
}
}
}

private extension View {
func discountTypeSelectionSheet(
isPresented: Binding<Bool>,
onSelection: @escaping (POSCouponDiscountType) -> Void
) -> some View {
sheet(isPresented: isPresented) {
let command = DiscountTypeBottomSheetListSelectorCommand(selected: nil) { type in
onSelection(.init(discountType: type))
}

NavigationView {
BottomSheetListSelector(
viewProperties: BottomSheetListSelectorViewProperties(),
command: command,
onDismiss: { _ in
isPresented.wrappedValue = false
}
)
.navigationBarTitleDisplayMode(.large)
.navigationTitle(Localization.selectCouponTypeTitle)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button(Localization.selectCouponCancelButtonTitle) {
isPresented.wrappedValue = false
}
}
}
}
.navigationViewStyle(.stack)
.interactiveDismissDisabled()
}
}
}

private struct POSCouponDiscountType: Identifiable, Equatable {
var id: String { discountType.rawValue }
let discountType: Coupon.DiscountType
}

private enum Localization {
static let selectCouponTypeTitle = NSLocalizedString(
"pos.couponCreationSheet.selectCoupon.title",
value: "Create coupon",
comment: "A title for the view that selects the type of coupon to create"
)

static let selectCouponCancelButtonTitle = NSLocalizedString(
"pos.couponCreationSheet.selectCoupon.cancel",
value: "Cancel",
comment: "A button that dismisses coupon creation sheet"
)
}
21 changes: 20 additions & 1 deletion WooCommerce/Classes/POS/Presentation/ItemListView.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import SwiftUI
import enum Yosemite.POSItem
import protocol Yosemite.POSOrderableItem
import struct Yosemite.POSCoupon

@available(iOS 17.0, *)
struct ItemListView: View {
Expand All @@ -23,6 +22,8 @@ struct ItemListView: View {

@State private var selectedItemType: ItemType = .products

@State private var showCouponCreationModal: Bool = false

var body: some View {
if #available(iOS 18.0, *) {
NavigationStack {
Expand Down Expand Up @@ -52,7 +53,19 @@ struct ItemListView: View {
}, label: {
Text("Coupons")
})

Spacer()

Button(action: {
showCouponCreationModal = true
}, label: {
Text(Image(systemName: "plus.circle.fill"))
})
.font(.posButtonSymbolLarge)
.foregroundStyle(Color.posOnSurface)
.renderedIf(selectedItemType == .coupons)
}
.padding(POSPadding.medium)
.renderedIf(shouldShowCoupons)

switch itemListState {
Expand All @@ -76,6 +89,12 @@ struct ItemListView: View {
.posModal(isPresented: $showSimpleProductsModal) {
SimpleProductsOnlyInformation(isPresented: $showSimpleProductsModal)
}
.posCouponCreationSheet(isPresented: $showCouponCreationModal, onSuccess: { couponItem in
Task { @MainActor in
posModel.addToCart(couponItem)
await posModel.refreshItems(base: .root)
}
})
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ final class AddEditCouponViewModel: ObservableObject {

/// Init method for coupon creation
///
init(siteID: Int64,
init(siteID: Int64 = ServiceLocator.stores.sessionManager.defaultStoreID ?? 0,
discountType: Coupon.DiscountType,
stores: StoresManager = ServiceLocator.stores,
storageManager: StorageManagerType = ServiceLocator.storageManager,
Expand Down
12 changes: 12 additions & 0 deletions WooCommerce/WooCommerce.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
015D99AA2C58C780001D7186 /* PointOfSaleCardPresentPaymentLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015D99A92C58C780001D7186 /* PointOfSaleCardPresentPaymentLayout.swift */; };
01620C4E2C5394B200D3EA2F /* POSProgressViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01620C4D2C5394B200D3EA2F /* POSProgressViewStyle.swift */; };
01664F9E2C50E685007CB5DD /* POSFontStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01664F9D2C50E685007CB5DD /* POSFontStyle.swift */; };
016A77692D9D24B00004FCD6 /* POSCouponCreationSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016A77682D9D24A70004FCD6 /* POSCouponCreationSheet.swift */; };
016C6B972C74AB17000D86FD /* POSConnectivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016C6B962C74AB17000D86FD /* POSConnectivityView.swift */; };
0174DDBB2CE5FD60005D20CA /* ReceiptEmailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0174DDBA2CE5FD5D005D20CA /* ReceiptEmailViewModel.swift */; };
0174DDBF2CE600C5005D20CA /* ReceiptEmailViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0174DDBE2CE600C0005D20CA /* ReceiptEmailViewModelTests.swift */; };
Expand Down Expand Up @@ -3275,6 +3276,7 @@
015D99A92C58C780001D7186 /* PointOfSaleCardPresentPaymentLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentLayout.swift; sourceTree = "<group>"; };
01620C4D2C5394B200D3EA2F /* POSProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSProgressViewStyle.swift; sourceTree = "<group>"; };
01664F9D2C50E685007CB5DD /* POSFontStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSFontStyle.swift; sourceTree = "<group>"; };
016A77682D9D24A70004FCD6 /* POSCouponCreationSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSCouponCreationSheet.swift; sourceTree = "<group>"; };
016C6B962C74AB17000D86FD /* POSConnectivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSConnectivityView.swift; sourceTree = "<group>"; };
0174DDBA2CE5FD5D005D20CA /* ReceiptEmailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptEmailViewModel.swift; sourceTree = "<group>"; };
0174DDBE2CE600C0005D20CA /* ReceiptEmailViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptEmailViewModelTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -6565,6 +6567,14 @@
path = "Reusable Views";
sourceTree = "<group>";
};
016A77672D9D24A30004FCD6 /* Coupons */ = {
isa = PBXGroup;
children = (
016A77682D9D24A70004FCD6 /* POSCouponCreationSheet.swift */,
);
path = Coupons;
sourceTree = "<group>";
};
0174DDB92CE5FD49005D20CA /* ReceiptEmail */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -7241,6 +7251,7 @@
026826A12BF59DED0036F959 /* Presentation */ = {
isa = PBXGroup;
children = (
016A77672D9D24A30004FCD6 /* Coupons */,
026A50262D2F6BBF002C42C2 /* Infinite Scroll */,
029149792D2682DF00F7B3B3 /* Item Selector */,
01620C4C2C5394A400D3EA2F /* Reusable Views */,
Expand Down Expand Up @@ -15817,6 +15828,7 @@
EE9D03122B89DF760077CED1 /* WooAnalyticsEvent+OrdersListFilter.swift in Sources */,
683421642ACE9391009021D7 /* ProductDiscountView.swift in Sources */,
B6E851F3276320C70041D1BA /* RefundCustomAmountsDetailsViewModel.swift in Sources */,
016A77692D9D24B00004FCD6 /* POSCouponCreationSheet.swift in Sources */,
024DF31F23743045006658FE /* Header+AztecFormatting.swift in Sources */,
B5A8F8AD20B88D9900D211DE /* LoginPrologueViewController.swift in Sources */,
B5D1AFC620BC7B7300DB0E8C /* StorePickerViewController.swift in Sources */,
Expand Down