diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooCarrierPackagesSelectionView.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooCarrierPackagesSelectionView.swift index 45aeb410cd4..e9ff751f1a3 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooCarrierPackagesSelectionView.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooCarrierPackagesSelectionView.swift @@ -36,7 +36,7 @@ struct WooCarrierPackagesView: View { Section { ForEach(packageGroup.packages, id: \.id) { package in PackageOptionView( - isSelected: selectedPackageId == package.id, // Check if this package is selected + isSelected: selectedPackageId == package.id, package: package, showTopDivider: false, showType: false, diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooSavedPackagesSelectionView.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooSavedPackagesSelectionView.swift index 5dd56867b1e..08635087a91 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooSavedPackagesSelectionView.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooSavedPackagesSelectionView.swift @@ -31,41 +31,61 @@ struct WooSavedPackagesSelectionView: View { VStack(spacing: 0) { Divider() List { - ForEach(viewModel.packages, id: \.id) { package in - PackageOptionView( - isSelected: viewModel.selectedPackageId == package.id, // Check if this package is selected - package: package, - showTopDivider: false, - showType: true, - tapAction: { - viewModel.selectedPackageId = viewModel.selectedPackageId == package.id ? nil : package.id - } - ) - .alignmentGuide(.listRowSeparatorLeading) { _ in - return 16 - } - .swipeActions { - Button { - // remove package - } label: { - Image(systemName: "trash") - } - .tint(Color.withColorStudio(name: .red, shade: .shade50)) - } - } - .listRowInsets(.zero) + packagesSection(for: viewModel.customPackages) + packagesSection(for: viewModel.predefinedPackages) } .listStyle(.plain) Divider() Button(WooShippingAddPackageView.Localization.addPackage) { addPackageButtonTapped() } - .disabled(viewModel.selectedPackageId == nil || viewModel.packages.isEmpty) + .disabled(viewModel.selectedPackageId == nil || !viewModel.hasPackages) .buttonStyle(PrimaryButtonStyle()) .padding() } } + @ViewBuilder + private func packagesSection(for packages: [any WooPackageDataRepresentable]) -> some View { + if packages.isEmpty { + EmptyView() + } + else { + Section { + packagesRows(for: packages) + } + .listRowInsets(.zero) + } + } + + private func packagesRows(for packages: [any WooPackageDataRepresentable]) -> some View { + ForEach(packages, id: \.id) { package in + PackageOptionView( + isSelected: viewModel.selectedPackageId == package.id, + package: package, + showTopDivider: false, + showType: true, + tapAction: { + viewModel.selectedPackageId = viewModel.selectedPackageId == package.id ? nil : package.id + } + ) + .alignmentGuide(.listRowSeparatorLeading) { _ in + return 16 + } + .swipeActions { + Button { + withAnimation { + viewModel.removePackage(package) + } + } label: { + Image(systemName: "trash") + } + .tint(Color.withColorStudio(name: .red, shade: .shade50)) + } + } + .listRowInsets(.zero) + } + private func addPackageButtonTapped() { // call addPackageAction with data from selected package guard let selectedPackage = viewModel.selectedPackage else { return } diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooSavedPackagesSelectionViewModel.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooSavedPackagesSelectionViewModel.swift index e6fb8a1bb19..0fe82f78eb5 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooSavedPackagesSelectionViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooSavedPackagesSelectionViewModel.swift @@ -1,16 +1,25 @@ import Foundation final class WooSavedPackagesSelectionViewModel: ObservableObject { - let packages: [any WooPackageDataRepresentable] + @Published var customPackages: [any WooPackageDataRepresentable] + @Published var predefinedPackages: [any WooPackageDataRepresentable] @Published var selectedPackageId: UUID? = nil // Track the selected package index - init(packages: [any WooPackageDataRepresentable]) { - self.packages = packages + init(customPackages: [any WooPackageDataRepresentable], + predefinedPackages: [any WooPackageDataRepresentable]) { + self.customPackages = customPackages + self.predefinedPackages = predefinedPackages + } + + var hasPackages: Bool { + return customPackages.isNotEmpty || predefinedPackages.isNotEmpty } var selectedPackage: WooPackageDataRepresentable? { guard let selectedPackageId else { return nil } + let packages = customPackages + predefinedPackages + for packageItem in packages { if selectedPackageId == packageItem.id { return packageItem @@ -19,4 +28,13 @@ final class WooSavedPackagesSelectionViewModel: ObservableObject { return nil } + + func removePackage(_ packageToRemove: WooPackageDataRepresentable) { + customPackages.removeAll { package in package.id == packageToRemove.id } + predefinedPackages.removeAll { package in package.id == packageToRemove.id } + + if selectedPackageId == packageToRemove.id { + selectedPackageId = nil + } + } } diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooShippingAddPackageView.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooShippingAddPackageView.swift index 947f33b9ca8..eb306f2d347 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooShippingAddPackageView.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Package and Rate Selection/WooShippingAddPackageView.swift @@ -108,37 +108,38 @@ struct WooShippingAddPackageView: View { } } + private let savedPackagesViewModel = WooSavedPackagesSelectionViewModel(customPackages: [ + WooSavedPackageData(name: "Small Flat Rate Box", + type: "Custom package", + packageType: "box", + dimensions: "21.92 × 13.67 × 4.14 cm", + weight: "5 kg"), + WooSavedPackageData(name: "Small Flat Rate Box", + type: "Custom package", + packageType: "box", + dimensions: "21.92 × 13.67 × 4.14 cm", + weight: "5 kg"), + WooSavedPackageData(name: "Small Flat Rate Box", + type: "Custom package", + packageType: "box", + dimensions: "21.92 × 13.67 × 4.14 cm", + weight: "5 kg"), + ], predefinedPackages: [ + WooSavedPackageData(name: "Small Flat Rate Box", + type: "DHL Express", + packageType: "box", + dimensions: "21.92 × 13.67 × 4.14 cm", + weight: "5 kg"), + WooSavedPackageData(name: "Small Flat Rate Box", + type: "USPS Priority Mail Flat Rate Boxes", + packageType: "box", + dimensions: "21.92 × 13.67 × 4.14 cm", + weight: "5 kg"), + ]) + @ViewBuilder private var savedPackageView: some View { - // TODO: dummy data for UI creation - let packages: [WooSavedPackageData] = [ - WooSavedPackageData(name: "Small Flat Rate Box", - type: "Custom package", - packageType: "box", - dimensions: "21.92 × 13.67 × 4.14 cm", - weight: "5 kg"), - WooSavedPackageData(name: "Small Flat Rate Box", - type: "DHL Express", - packageType: "box", - dimensions: "21.92 × 13.67 × 4.14 cm", - weight: "5 kg"), - WooSavedPackageData(name: "Small Flat Rate Box", - type: "Custom package", - packageType: "box", - dimensions: "21.92 × 13.67 × 4.14 cm", - weight: "5 kg"), - WooSavedPackageData(name: "Small Flat Rate Box", - type: "USPS Priority Mail Flat Rate Boxes", - packageType: "box", - dimensions: "21.92 × 13.67 × 4.14 cm", - weight: "5 kg"), - WooSavedPackageData(name: "Small Flat Rate Box", - type: "Custom package", - packageType: "box", - dimensions: "21.92 × 13.67 × 4.14 cm", - weight: "5 kg"), - ] - WooSavedPackagesSelectionView(viewModel: WooSavedPackagesSelectionViewModel(packages: packages)) { packageData in + WooSavedPackagesSelectionView(viewModel: savedPackagesViewModel) { packageData in addPackageAction(packageData) } } diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooSavedPackagesSelectionViewModelTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooSavedPackagesSelectionViewModelTests.swift index 8aa81aa2e77..d814a275ce0 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooSavedPackagesSelectionViewModelTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/WooSavedPackagesSelectionViewModelTests.swift @@ -5,37 +5,89 @@ import Yosemite final class WooSavedPackagesSelectionViewModelTests: XCTestCase { func test_it_inits_packages() { // Given/When - let packages: [WooSavedPackageData] = testingPackages() - let viewModel = WooSavedPackagesSelectionViewModel(packages: packages) + let viewModel = WooSavedPackagesSelectionViewModel(customPackages: testingCustomPackages(), + predefinedPackages: testingPredefinedPackages()) // Then - XCTAssertEqual(viewModel.packages.count, 2) + XCTAssertEqual(viewModel.customPackages.count, 2) + XCTAssertEqual(viewModel.predefinedPackages.count, 1) XCTAssertNil(viewModel.selectedPackageId) XCTAssertNil(viewModel.selectedPackage) } func test_it_selects_package() { // Given - let packages: [WooSavedPackageData] = testingPackages() - let viewModel = WooSavedPackagesSelectionViewModel(packages: packages) + let customPackages = testingCustomPackages() + let viewModel = WooSavedPackagesSelectionViewModel(customPackages: customPackages, + predefinedPackages: testingPredefinedPackages()) // When - viewModel.selectedPackageId = packages.first?.id + viewModel.selectedPackageId = customPackages.first?.id // Then XCTAssertNotNil(viewModel.selectedPackageId) XCTAssertNotNil(viewModel.selectedPackage) } + + func test_it_removes_package() { + // Given + let customPackages = testingCustomPackages() + let customPackagesCount = customPackages.count + let viewModel = WooSavedPackagesSelectionViewModel(customPackages: customPackages, + predefinedPackages: testingPredefinedPackages()) + + // When + if let package = customPackages.first { + viewModel.removePackage(package) + } + + // Then + XCTAssertEqual(viewModel.customPackages.count, customPackagesCount - 1) + } + + func test_it_removes_selected_package() { + // Given + let customPackages = testingCustomPackages() + let customPackagesCount = customPackages.count + let viewModel = WooSavedPackagesSelectionViewModel(customPackages: customPackages, + predefinedPackages: testingPredefinedPackages()) + + // When + viewModel.selectedPackageId = customPackages.first?.id + + // Then + XCTAssertNotNil(viewModel.selectedPackageId) + XCTAssertNotNil(viewModel.selectedPackage) + + // When + if let package = customPackages.first { + viewModel.removePackage(package) + } + + // Then + XCTAssertEqual(viewModel.customPackages.count, customPackagesCount - 1) + XCTAssertNil(viewModel.selectedPackageId) + XCTAssertNil(viewModel.selectedPackage) + } } extension WooSavedPackagesSelectionViewModelTests { - func testingPackages() -> [WooSavedPackageData] { + func testingCustomPackages() -> [WooSavedPackageData] { return [ WooSavedPackageData(name: "Small Flat Rate Box", type: "Custom package", packageType: "box", dimensions: "21.92 × 13.67 × 4.14 cm", weight: "5 kg"), + WooSavedPackageData(name: "Small Flat Rate Box", + type: "Custom package", + packageType: "box", + dimensions: "21.92 × 13.67 × 4.14 cm", + weight: "5 kg") + ] + } + func testingPredefinedPackages() -> [WooSavedPackageData] { + return [ WooSavedPackageData(name: "Small Flat Rate Box", type: "DHL Express", packageType: "box",