diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/WooShippingSplitShipmentsDetailView.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/WooShippingSplitShipmentsDetailView.swift new file mode 100644 index 00000000000..8292dfa82be --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/WooShippingSplitShipmentsDetailView.swift @@ -0,0 +1,76 @@ +import SwiftUI + +struct WooShippingSplitShipmentsDetailView: View { + @Environment(\.dismiss) private var dismiss + + let viewModel: WooShippingSplitShipmentsViewModel + + var body: some View { + NavigationView { + ScrollView { + VStack(alignment: .leading, spacing: Layout.contentPadding) { + AdaptiveStack(horizontalAlignment: .leading) { + Text(viewModel.itemsCountLabel) + .headlineStyle() + Spacer() + Text(viewModel.itemsDetailLabel) + .foregroundStyle(Color(.textSubtle)) + } + + VStack { + ForEach(viewModel.items) { item in + WooShippingItemRow(viewModel: item) + .padding() + .roundedBorder(cornerRadius: Layout.borderCornerRadius, lineColor: Color(.separator), lineWidth: Layout.borderWidth) + } + } + } + .padding(Layout.contentPadding) + } + .navigationBarTitleDisplayMode(.inline) + .navigationTitle(Localization.title) + .toolbar { + ToolbarItem(placement: .cancellationAction) { + Button(Localization.selectAll) { + + } + } + ToolbarItem(placement: .confirmationAction) { + Button(Localization.done) { + dismiss() + } + } + } + } + } +} + +private extension WooShippingSplitShipmentsDetailView { + enum Layout { + static let contentPadding: CGFloat = 16 + static let borderCornerRadius: CGFloat = 8 + static let borderWidth: CGFloat = 0.5 + } + enum Localization { + static let title = NSLocalizedString( + "wooShippingSplitShipmentsDetailView.title", + value: "Split Shipments", + comment: "Title of the split shipments detail view in the shipping label creation flow" + ) + static let selectAll = NSLocalizedString( + "wooShippingSplitShipmentsDetailView.selectAll", + value: "Select All", + comment: "Button to select all items in the shipment detail in the shipping label creation flow" + ) + static let done = NSLocalizedString( + "wooShippingSplitShipmentsDetailView.done", + value: "Done", + comment: "Button to save split shipment configurations in the shipping label creation flow" + ) + } +} + +#Preview { + WooShippingSplitShipmentsDetailView(viewModel: WooShippingSplitShipmentsViewModel(siteID: 123, + orderID: 123)) +} diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/WooShippingSplitShipmentsRow.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/WooShippingSplitShipmentsRow.swift new file mode 100644 index 00000000000..4a3d4acc88d --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/WooShippingSplitShipmentsRow.swift @@ -0,0 +1,49 @@ +import SwiftUI + +struct WooShippingSplitShipmentsRow: View { + @State private var isShowingDetailView = false + + let viewModel: WooShippingSplitShipmentsViewModel + + init(viewModel: WooShippingSplitShipmentsViewModel) { + self.viewModel = viewModel + } + + var body: some View { + AdaptiveStack { + Text(Localization.products) + .tertiaryTitleStyle() + Spacer() + Button(Localization.splitShipments) { + isShowingDetailView = true + } + .buttonStyle(TextButtonStyle()) + } + .padding(.vertical, Layout.verticalPadding) + .fullScreenCover(isPresented: $isShowingDetailView) { + WooShippingSplitShipmentsDetailView(viewModel: viewModel) + } + } +} + +private extension WooShippingSplitShipmentsRow { + enum Layout { + static let verticalPadding: CGFloat = 16 + } + + enum Localization { + static let products = NSLocalizedString("wooShipping.splitShipments.products", + value: "Products", + comment: "Label for section in shipping label creation to split shipments.") + + static let splitShipments = NSLocalizedString("wooShipping.splitShipments.splitShipmentsButtonTitle", + value: "Split shipments", + comment: "Title for button in shipping label creation to start split shipments flow.") + } +} + +#Preview { + WooShippingSplitShipmentsRow(viewModel: WooShippingSplitShipmentsViewModel(siteID: 123, + orderID: 123)) + .padding() +} diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/WooShippingSplitShipmentsViewModel.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/WooShippingSplitShipmentsViewModel.swift new file mode 100644 index 00000000000..4ba93c6c5f4 --- /dev/null +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/WooShippingSplitShipmentsViewModel.swift @@ -0,0 +1,35 @@ +import SwiftUI +import Yosemite +import WooFoundation + +final class WooShippingSplitShipmentsViewModel: ObservableObject { + private let siteID: Int64 + private let orderID: Int64 + private let stores: StoresManager + + /// Label for the total number of items + let itemsCountLabel = "6 items" + + /// Label for the total item details + let itemsDetailLabel = "825g · $135.00" + + let items: [WooShippingItemRowViewModel] = [WooShippingItemRowViewModel(imageUrl: nil, + quantityLabel: "3", + name: "Little Nap Brazil 250g", + detailsLabel: "15×10×8cm • Espresso", + weightLabel: "275g", + priceLabel: "$60.00"), + WooShippingItemRowViewModel(imageUrl: nil, + quantityLabel: "3", + name: "Little Nap Brazil 250g", + detailsLabel: "15×10×8cm • Espresso", + weightLabel: "275g", + priceLabel: "$60.00")] + init(siteID: Int64, + orderID: Int64, + stores: StoresManager = ServiceLocator.stores) { + self.siteID = siteID + self.orderID = orderID + self.stores = stores + } +} diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShippingCreateLabelsView.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShippingCreateLabelsView.swift index a739e2d70fa..20359e2d229 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShippingCreateLabelsView.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShippingCreateLabelsView.swift @@ -104,6 +104,10 @@ private extension WooShippingCreateLabelsView { WooShippingPostPurchaseView(viewModel: postPurchase) } + if let splitShipmentsViewModel = viewModel.splitShipmentsViewModel { + WooShippingSplitShipmentsRow(viewModel: splitShipmentsViewModel) + } + WooShippingItems(viewModel: viewModel.items) WooShippingHazmatRow(isHazardous: $viewModel.containsHazardousMaterials, diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShippingCreateLabelsViewModel.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShippingCreateLabelsViewModel.swift index a933b2546c4..f90ba83f1c8 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShippingCreateLabelsViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShippingCreateLabelsViewModel.swift @@ -62,6 +62,9 @@ final class WooShippingCreateLabelsViewModel: ObservableObject { /// View model for the label shipping service. private(set) var shippingService: WooShippingServiceViewModel? + /// View model for split shipments. + private(set) var splitShipmentsViewModel: WooShippingSplitShipmentsViewModel? + /// Selected shipping rate when creating a shipping label. @Published private var selectedRate: WooShippingSelectedRate? @@ -269,6 +272,10 @@ final class WooShippingCreateLabelsViewModel: ObservableObject { await self.loadOriginAddresses() } } + + group.addTask { + await self.loadShipmentsInfo() + } } if isMissingStoreSettings || @@ -416,6 +423,15 @@ private extension WooShippingCreateLabelsViewModel { } } + /// Loads shipment info from remote and creates view model for split shipments. + /// + @MainActor + func loadShipmentsInfo() async { + splitShipmentsViewModel = WooShippingSplitShipmentsViewModel(siteID: order.siteID, + orderID: order.orderID, + stores: stores) + } + /// Loads destination address of the order from remote. /// func loadDestinationAddress() { diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 73437df3f9b..bf0aecc84ae 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -2992,6 +2992,9 @@ EE66BB102B29D24600518DAF /* DefaultThemeInstallerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66BB0F2B29D24600518DAF /* DefaultThemeInstallerTests.swift */; }; EE66BB122B29D65400518DAF /* MockThemeInstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66BB112B29D65400518DAF /* MockThemeInstaller.swift */; }; EE6C6B6E2C65DC4100632BDA /* WordPressMediaLibraryPickerDataSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C6B6D2C65DC4100632BDA /* WordPressMediaLibraryPickerDataSourceTests.swift */; }; + EE7E75A82D83EB1F00E6FF5B /* WooShippingSplitShipmentsRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE7E75A72D83EB0700E6FF5B /* WooShippingSplitShipmentsRow.swift */; }; + EE7E75AA2D84066C00E6FF5B /* WooShippingSplitShipmentsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE7E75A92D84066800E6FF5B /* WooShippingSplitShipmentsDetailView.swift */; }; + EE7E75AC2D84080D00E6FF5B /* WooShippingSplitShipmentsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE7E75AB2D84080A00E6FF5B /* WooShippingSplitShipmentsViewModel.swift */; }; EE81B1382865BB0B0032E0D4 /* ProductImagesProductIDUpdaterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE81B1372865BB0B0032E0D4 /* ProductImagesProductIDUpdaterTests.swift */; }; EE8A302B2B70B63E001D7C66 /* MockImageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE8A302A2B70B63E001D7C66 /* MockImageService.swift */; }; EE8A30452B74948C001D7C66 /* OrderAttributionInfo+Origin.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE8A30442B74948C001D7C66 /* OrderAttributionInfo+Origin.swift */; }; @@ -6193,6 +6196,9 @@ EE66BB0F2B29D24600518DAF /* DefaultThemeInstallerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultThemeInstallerTests.swift; sourceTree = ""; }; EE66BB112B29D65400518DAF /* MockThemeInstaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockThemeInstaller.swift; sourceTree = ""; }; EE6C6B6D2C65DC4100632BDA /* WordPressMediaLibraryPickerDataSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordPressMediaLibraryPickerDataSourceTests.swift; sourceTree = ""; }; + EE7E75A72D83EB0700E6FF5B /* WooShippingSplitShipmentsRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingSplitShipmentsRow.swift; sourceTree = ""; }; + EE7E75A92D84066800E6FF5B /* WooShippingSplitShipmentsDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingSplitShipmentsDetailView.swift; sourceTree = ""; }; + EE7E75AB2D84080A00E6FF5B /* WooShippingSplitShipmentsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingSplitShipmentsViewModel.swift; sourceTree = ""; }; EE81B1372865BB0B0032E0D4 /* ProductImagesProductIDUpdaterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductImagesProductIDUpdaterTests.swift; sourceTree = ""; }; EE8A302A2B70B63E001D7C66 /* MockImageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockImageService.swift; sourceTree = ""; }; EE8A30442B74948C001D7C66 /* OrderAttributionInfo+Origin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrderAttributionInfo+Origin.swift"; sourceTree = ""; }; @@ -12457,6 +12463,7 @@ CEAB739A2C81E3A000A7EB39 /* WooShipping Create Shipping Labels */ = { isa = PBXGroup; children = ( + EE7E75A62D83EAD200E6FF5B /* WooShipping Split Shipments */, B90DD08C2D12FA6600EFC06A /* WooShipping Customs */, CE6E11092C91DA3D00563DD4 /* WooShipping Items Section */, CE7B4A592CA1BF7800F764EB /* WooShipping Hazmat Section */, @@ -13963,6 +13970,16 @@ path = AIToneVoice; sourceTree = ""; }; + EE7E75A62D83EAD200E6FF5B /* WooShipping Split Shipments */ = { + isa = PBXGroup; + children = ( + EE7E75AB2D84080A00E6FF5B /* WooShippingSplitShipmentsViewModel.swift */, + EE7E75A92D84066800E6FF5B /* WooShippingSplitShipmentsDetailView.swift */, + EE7E75A72D83EB0700E6FF5B /* WooShippingSplitShipmentsRow.swift */, + ); + path = "WooShipping Split Shipments"; + sourceTree = ""; + }; EE8B420A2BF9A1E40077C4E7 /* Orders */ = { isa = PBXGroup; children = ( @@ -16514,6 +16531,7 @@ 26E0AE13263359F900A5EB3B /* View+Conditionals.swift in Sources */, 68F151E12C0DA7910082AEC8 /* CartItem.swift in Sources */, CE583A072107849F00D73C1C /* SwitchTableViewCell.swift in Sources */, + EE7E75A82D83EB1F00E6FF5B /* WooShippingSplitShipmentsRow.swift in Sources */, D8149F562251EE300006A245 /* UITextField+Helpers.swift in Sources */, 20762BA52C18B42C00758305 /* CardPresentPaymentBluetoothReaderConnectionAlertsProvider.swift in Sources */, 0212276124498A270042161F /* ProductFormBottomSheetListSelectorCommand.swift in Sources */, @@ -16624,6 +16642,7 @@ 025FDD3223717D2900824006 /* EditorFactory.swift in Sources */, 45D1CF4723BAC89A00945A36 /* ProductTaxStatusListSelectorCommand.swift in Sources */, 453DBF9023882814006762A5 /* ProductImagesFlowLayout.swift in Sources */, + EE7E75AA2D84066C00E6FF5B /* WooShippingSplitShipmentsDetailView.swift in Sources */, 024DF30E23742A70006658FE /* AztecBoldFormatBarCommand.swift in Sources */, DEE6437826D8DAD900888A75 /* InProgressView.swift in Sources */, 0290E275238E4F8100B5C466 /* PaginatedListSelectorViewController.swift in Sources */, @@ -17216,6 +17235,7 @@ 02DE5CA9279F857D007CBEF3 /* Double+Rounding.swift in Sources */, B9CCE5FC2C8753A000905A91 /* ProductBarcodeScannerProvider.swift in Sources */, 02A65301246AA63600755A01 /* ProductDetailsFactory.swift in Sources */, + EE7E75AC2D84080D00E6FF5B /* WooShippingSplitShipmentsViewModel.swift in Sources */, D449C51D26DE6B5000D75B02 /* LargeTitle.swift in Sources */, B6E7DB64293A7C390049B001 /* AnalyticsHubYesterdayRangeData.swift in Sources */, 456396B625C82691001F1A26 /* ShippingLabelFormStepTableViewCell.swift in Sources */,