Repository: woocommerce/woocommerce-ios
Pull request #15397: [Shipping Labels] Split shipment initial UI
Author: login: selanthiraiyan
Labels: name: feature: shipping labels
description: Related to creating, ordering, or printing shipping labels.
Created: 2025-03-23T10:49:34Z | Merged: 2025-03-24T06:18:35Z

<!-- Remember about a good descriptive title. -->

Part of: #15305 
<!-- Id number of the GitHub issue this PR addresses. -->
<!-- Part of: # -> use this if your PR is one of many related to one issue -->

<!-- 
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🛠 Need an AI review?  
Add a comment: `@coderabbitai review`
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-->

## Description
<!-- Take the time to write a good summary. Why is it needed? What does it do? When fixing bugs try to avoid just writing “See original issue” – clarify what the problem was and how you’ve fixed it. -->

Adds the initial UI for the split shipments screen. 

Design

Internal - bsLNrhmi2xQZB4C0TzjmHB-fi-942_43007

<img width="312" alt="image" src="https://github.com/user-attachments/assets/1fb0007a-33d2-42bd-87e5-1f6a9a6498c3" />


Changes
- New selectable row and card SwiftUI views to have selectable and collapsible shipment items views. 
- Lists the shipment items from the order in to a collapsible list. 
- Unit tests.

The changes in this PR focus only on the UI. The selection logic will be added/altered in future PRs. 

## Steps to reproduce
<!-- Step-by-step testing instructions. For new user flows, consider instead stating the goal of the workflow and see if your PR reviewer can accomplish the workflow without specific steps! -->


1. Log in to a test store with the Woo Shipping plugin set up.
1. Create an order with multiple products and different quantities for each product.
1. Enter a valid shipping address for the order.
1. Open the order detail page. 
1. Tap "Create shipping label".
1. Tap "Split shipments".
1. Validate that the screen matches the given design. 
1. Select, expand and collapse the shipment items. It should look as per the design. Internal - bsLNrhmi2xQZB4C0TzjmHB-fi-942_43007

## Testing information
<!-- This is your opportunity to break out individual scenarios that need testing (when necessary) and/or include a checklist for the reviewer to go through. Consider documenting the following from your own completed testing: devices used, alternate workflows, edge cases, affected areas, critical flows, areas not tested, and any remaining unknowns. Provide feedback on this new section of the PR template through Sept 30, 2024 to Apps Quality; additional context here: p91TBi-b8z-p2#comment-12036 -->

Validated that the expansion, collapse, and selection steps look as per the design. 


## Screenshots
<!-- Include before and after images or gifs when appropriate. -->

### Screenshots

| Light | Dark |
|--------|--------|
| ![Simulator Screenshot - iPhone 16 Pro - 2025-03-23 at 16 30 40](https://github.com/user-attachments/assets/527dedf2-a622-4556-a303-b70b07c783c2) | ![Simulator Screenshot - iPhone 16 Pro - 2025-03-23 at 16 30 32](https://github.com/user-attachments/assets/155a423e-d059-4491-a35d-741968662229) | 

### Screen recording

https://github.com/user-attachments/assets/0c558dd5-5955-421b-9d10-6cc7e6f43746

---
- [x] I have considered if this change warrants user-facing release notes and have added them to `RELEASE-NOTES.txt` if necessary.

## Reviewer (or Author, in the case of optional code reviews):

Please make sure these conditions are met before approving the PR, or request changes if the PR needs improvement:

- [ ] The PR is small and has a clear, single focus, or a valid explanation is provided in the description. If needed, please request to split it into smaller PRs.
- [ ] Ensure Adequate Unit Test Coverage: The changes are reasonably covered by unit tests or an explanation is provided in the PR description.
- [ ] Manual Testing: The author listed all the tests they ran, including smoke tests when needed (e.g., for refactorings). The reviewer confirmed that the PR works as expected on all devices (phone/tablet) and no regressions are added.


Diff:
diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Items Section/WooShippingItemsViewModel.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Items Section/WooShippingItemsViewModel.swift
index cefaa4ea87f..5eb7f76993c 100644
--- a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Items Section/WooShippingItemsViewModel.swift	
+++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Items Section/WooShippingItemsViewModel.swift	
@@ -10,7 +10,7 @@ final class WooShippingItemsViewModel: ObservableObject {
     private let currencySettings: CurrencySettings
 
     /// Data source for items to be shipped.
-    private var dataSource: WooShippingItemsDataSource
+    private(set) var dataSource: WooShippingItemsDataSource
 
     /// Label with the total number of items to ship.
     @Published private(set) var itemsCountLabel: String = ""
diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCard.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCard.swift
new file mode 100644
index 00000000000..da9e9817c75
--- /dev/null
+++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCard.swift	
@@ -0,0 +1,97 @@
+import Yosemite
+import SwiftUI
+
+/// Displays a single collapsible shipment item row or grouped parent and child shipment item rows
+struct CollapsibleShipmentCard: View {
+    @State private var isCollapsed: Bool = true
+
+    private let viewModel: CollapsibleShipmentCardViewModel
+
+    init(viewModel: CollapsibleShipmentCardViewModel) {
+        self.viewModel = viewModel
+    }
+
+    var body: some View {
+        VStack(alignment: .leading, spacing: 0) {
+            mainShipmentRow
+                .padding(.horizontal, Layout.horizontalPadding)
+                .padding(.vertical, Layout.verticalPadding)
+                .background(
+                    mainShipmentRowBackground
+                )
+
+            if !isCollapsed {
+                VStack(spacing: 0) {
+                    ForEach(Array(viewModel.childShipmentRows.enumerated()), id: \.element.id) { index, item in
+                        VStack(spacing: 0) {
+                            Divider()
+
+                            SelectableShipmentRow(viewModel: item)
+                                .padding(.leading, Layout.horizontalPadding * 2)
+                                .padding(.trailing, Layout.horizontalPadding)
+                                .padding(.vertical, Layout.verticalPadding)
+                                .background(backgroundForChildShipmentRow(isFinalRow: index == viewModel.childShipmentRows.count - 1))
+                        }
+                    }
+                }
+            }
+        }
+        .frame(maxWidth: .infinity, alignment: .center)
+        .roundedBorder(cornerRadius: Layout.borderCornerRadius, lineColor: Color(.separator), lineWidth: Layout.borderWidth)
+    }
+}
+
+private extension CollapsibleShipmentCard {
+    @ViewBuilder
+    var mainShipmentRow: some View {
+        if viewModel.childShipmentRows.isEmpty {
+            SelectableShipmentRow(viewModel: viewModel.mainShipmentRow)
+        } else {
+            Button(action: {
+                withAnimation {
+                    isCollapsed.toggle()
+                }
+            }, label: {
+                ZStack(alignment: .topTrailing) {
+                    SelectableShipmentRow(viewModel: viewModel.mainShipmentRow)
+                        .contentShape(Rectangle())
+
+                    Image(uiImage: isCollapsed ? .chevronDownImage : .chevronUpImage)
+                        .foregroundColor(Color(.accent))
+                }
+            })
+            .buttonStyle(PlainButtonStyle())
+        }
+    }
+
+    @ViewBuilder
+    var mainShipmentRowBackground: some View {
+        if isCollapsed {
+            RoundedRectangle(cornerRadius: Layout.borderCornerRadius)
+                .fill(Color(.listForeground(modal: false)))
+        } else {
+            UnevenRoundedRectangle(cornerRadii: .init(topLeading: Layout.borderCornerRadius, topTrailing: Layout.borderCornerRadius))
+                .fill(Color(.listForeground(modal: false)))
+        }
+    }
+
+    @ViewBuilder
+    func backgroundForChildShipmentRow(isFinalRow: Bool) -> some View {
+        if isFinalRow {
+            UnevenRoundedRectangle(cornerRadii: .init(bottomLeading: Layout.borderCornerRadius, bottomTrailing: Layout.borderCornerRadius))
+                .fill(Color(.listForeground(modal: false)))
+        } else {
+            Color(.listForeground(modal: false))
+        }
+    }
+}
+
+private extension CollapsibleShipmentCard {
+    enum Layout {
+        static let borderCornerRadius: CGFloat = 8
+        static let borderWidth: CGFloat = 0.5
+        static let borderLineWidth: CGFloat = 1
+        static let horizontalPadding: CGFloat = 16
+        static let verticalPadding: CGFloat = 8
+    }
+}
diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCardViewModel.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCardViewModel.swift
new file mode 100644
index 00000000000..002cb367854
--- /dev/null
+++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCardViewModel.swift	
@@ -0,0 +1,60 @@
+import SwiftUI
+import WooFoundation
+import Yosemite
+
+/// View model for `CollapsibleShipmentCard`.
+final class CollapsibleShipmentCardViewModel: ObservableObject, Identifiable {
+    let id = UUID()
+
+    /// The main shipment row.
+    let mainShipmentRow: SelectableShipmentRowViewModel
+
+    /// Child shipment rows, if the shipment has more than one quantity
+    let childShipmentRows: [SelectableShipmentRowViewModel]
+
+    init(parentShipmentId: String,
+         childShipmentIds: [String],
+         item: ShippingLabelPackageItem,
+         currency: String) {
+        let mainShippingItem = WooShippingItemRowViewModel(item: ShippingLabelPackageItem(copy: item, quantity: max(1.0, Decimal(childShipmentIds.count))),
+                                                           currency: currency)
+        let childShippingItem = WooShippingItemRowViewModel(item: ShippingLabelPackageItem(copy: item, quantity: 1.0),
+                                                            currency: currency)
+
+        self.mainShipmentRow = SelectableShipmentRowViewModel(shipmentId: parentShipmentId,
+                                                              isSelectable: true,
+                                                              item: mainShippingItem,
+                                                              showQuantity: true)
+        self.childShipmentRows = childShipmentIds.map({
+            SelectableShipmentRowViewModel(shipmentId: $0,
+                                           isSelectable: true,
+                                           item: childShippingItem,
+                                           showQuantity: false)
+        })
+
+        observeSelection()
+    }
+
+    func selectAll() {
+        mainShipmentRow.setSelected(true)
+        childShipmentRows.forEach({ $0.setSelected(true) })
+    }
+}
+
+private extension CollapsibleShipmentCardViewModel {
+    func observeSelection() {
+        mainShipmentRow.onSelectedChange = { [weak self] row in
+            guard let self else { return }
+
+            childShipmentRows.forEach({ $0.setSelected(row.selected) })
+        }
+
+        childShipmentRows.forEach({
+            $0.onSelectedChange = { [weak self] row in
+                guard let self else { return }
+
+                mainShipmentRow.setSelected(false)
+            }
+        })
+    }
+}
diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/SelectableShipmentRow.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/SelectableShipmentRow.swift
new file mode 100644
index 00000000000..964554f35ab
--- /dev/null
+++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/SelectableShipmentRow.swift	
@@ -0,0 +1,96 @@
+import Yosemite
+import SwiftUI
+
+/// Row for a selectable shipment item to ship with the Woo Shipping extension.
+struct SelectableShipmentRow: View {
+    @ObservedObject private var viewModel: SelectableShipmentRowViewModel
+
+    init(viewModel: SelectableShipmentRowViewModel) {
+        self.viewModel = viewModel
+    }
+
+    @ScaledMetric private var scale: CGFloat = 1
+
+    var body: some View {
+        AdaptiveStack(spacing: Layout.horizontalSpacing) {
+            if viewModel.isSelectable {
+                selectionCircle(selected: viewModel.selected)
+                    .contentShape(Rectangle())
+                    .onTapGesture {
+                        viewModel.handleTap()
+                    }
+            }
+
+            ProductImageThumbnail(productImageURL: viewModel.item.imageUrl,
+                                  productImageSize: Layout.imageSize,
+                                  scale: scale,
+                                  productImageCornerRadius: Layout.imageCornerRadius,
+                                  foregroundColor: Color(UIColor.listSmallIcon))
+            .overlay(alignment: .topTrailing) {
+                if viewModel.showQuantity {
+                    BadgeView(text: viewModel.item.quantityLabel,
+                              customizations: .init(textColor: .white, backgroundColor: .black),
+                              backgroundShape: badgeStyle)
+                    .offset(x: Layout.badgeOffset, y: -Layout.badgeOffset)
+                }
+            }
+            VStack(alignment: .leading) {
+                Text(viewModel.item.name)
+                    .bodyStyle()
+                Text(viewModel.item.detailsLabel)
+                    .subheadlineStyle()
+                AdaptiveStack(verticalAlignment: .lastTextBaseline) {
+                    Text(viewModel.item.weightLabel)
+                        .subheadlineStyle()
+                    Spacer()
+                    Text(viewModel.item.priceLabel)
+                        .font(.subheadline)
+                        .foregroundStyle(Color(.text))
+                }
+            }
+        }
+        .frame(maxWidth: .infinity)
+    }
+}
+
+private extension SelectableShipmentRow {
+    @ViewBuilder
+    func selectionCircle(selected: Bool) -> some View {
+        if selected {
+            Image(uiImage: .checkCircleImage.withRenderingMode(.alwaysTemplate))
+                .foregroundStyle(Color(.primary))
+        } else {
+            Image(uiImage: .checkEmptyCircleImage)
+        }
+    }
+
+    /// Displays a different badge background shape based on the item quantity
+    /// Circular for 2-character quantities, rounded for 3-character quantities or more
+    var badgeStyle: BadgeView.BackgroundShape {
+        if viewModel.item.quantityLabel.count < 3 {
+            return .circle
+        } else {
+            return .roundedRectangle(cornerRadius: Layout.badgeOffset)
+        }
+    }
+}
+
+private extension SelectableShipmentRow {
+    enum Layout {
+        static let horizontalSpacing: CGFloat = 16
+        static let imageSize: CGFloat = 56.0
+        static let imageCornerRadius: CGFloat = 4.0
+        static let badgeOffset: CGFloat = 8.0
+    }
+}
+
+#Preview {
+    SelectableShipmentRow(viewModel: SelectableShipmentRowViewModel(shipmentId: "123",
+                                                                    isSelectable: false,
+                                                                    item: WooShippingItemRowViewModel(imageUrl: nil,
+                                                                                                      quantityLabel: "3",
+                                                                                                      name: "Little Nap Brazil 250g",
+                                                                                                      detailsLabel: "15×10×8cm • Espresso",
+                                                                                                      weightLabel: "275g",
+                                                                                                      priceLabel: "$60.00")))
+}
diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/SelectableShipmentRowViewModel.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/SelectableShipmentRowViewModel.swift
new file mode 100644
index 00000000000..071070de0a4
--- /dev/null
+++ b/WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/SelectableShipmentRowViewModel.swift	
@@ -0,0 +1,41 @@
+import SwiftUI
+import WooFoundation
+import Yosemite
+
+/// View model for `SelectableShipmentRow`.
+final class SelectableShipmentRowViewModel: ObservableObject, Identifiable {
+    let id = UUID()
+
+    let item: WooShippingItemRowViewModel
+
+    @Published private(set) var selected: Bool = false
+
+    let isSelectable: Bool
+
+    let shipmentId: String
+
+    let showQuantity: Bool
+
+    var onSelectedChange: ((SelectableShipmentRowViewModel) -> Void)?
+
+    init(shipmentId: String,
+         isSelectable: Bool,
+         item: WooShippingItemRowViewModel,
+         showQuantity: Bool = true) {
+        self.shipmentId = shipmentId
+        self.isSelectable = isSelectable
+        self.item = item
+        self.showQuantity = showQuantity
+    }
+
+    func handleTap() {
+        selected = !selected
+        if let onSelectedChange {
+            onSelectedChange(self)
+        }
+    }
+
+    func setSelected(_ selected: Bool) {
+        self.selected = selected
+    }
+}
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
index 16b9ef6d2ed..e3565ddbc1a 100644
--- 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	
@@ -1,4 +1,5 @@
 import SwiftUI
+import Yosemite
 
 struct WooShippingSplitShipmentsDetailView: View {
     @Environment(\.dismiss) private var dismiss
@@ -17,11 +18,9 @@ struct WooShippingSplitShipmentsDetailView: View {
                             .foregroundStyle(Color(.textSubtle))
                     }
 
-                    VStack {
-                        ForEach(viewModel.items) { item in
-                            WooShippingItemRow(viewModel: item)
-                                .padding()
-                                .roundedBorder(cornerRadius: Layout.borderCornerRadius, lineColor: Color(.separator), lineWidth: Layout.borderWidth)
+                    VStack(spacing: Layout.verticalSpacing) {
+                        ForEach(viewModel.shipmentCardViewModels) { item in
+                            CollapsibleShipmentCard(viewModel: item)
                         }
                     }
                 }
@@ -32,7 +31,7 @@ struct WooShippingSplitShipmentsDetailView: View {
             .toolbar {
                 ToolbarItem(placement: .cancellationAction) {
                     Button(Localization.selectAll) {
-
+                        viewModel.selectAll()
                     }
                 }
                 ToolbarItem(placement: .confirmationAction) {
@@ -50,6 +49,7 @@ private extension WooShippingSplitShipmentsDetailView {
         static let contentPadding: CGFloat = 16
         static let borderCornerRadius: CGFloat = 8
         static let borderWidth: CGFloat = 0.5
+        static let verticalSpacing: CGFloat = 8
     }
     enum Localization {
         static let title = NSLocalizedString(
@@ -73,6 +73,16 @@ private extension WooShippingSplitShipmentsDetailView {
 #if DEBUG
 #Preview {
     WooShippingSplitShipmentsDetailView(viewModel: WooShippingSplitShipmentsViewModel(order: ShippingLabelSampleData.sampleOrder(),
-                                                                                       config: ShippingLabelSampleData.sampleWooShippingConfig()))
+                                                                                      config: ShippingLabelSampleData.sampleWooShippingConfig(),
+                                                                                      items: [ShippingLabelPackageItem(productOrVariationID: 1,
+                                                                                                                       name: "Shirt",
+                                                                                                                       weight: 0.5,
+                                                                                                                       quantity: 2,
+                                                                                                                       value: 9.99,
+                                                                                                                       dimensions: ProductDimensions(length: "",
+                                                                                                                                                     width: "",
+                                                                                                                                                     height: ""),
+                                                                                                                       attributes: [],
+                                                                                                                       imageURL: nil)]))
 }
 #endif
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
index f2faf50226b..1c011b3afc0 100644
--- 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	
@@ -46,7 +46,17 @@ private extension WooShippingSplitShipmentsRow {
 #if DEBUG
 #Preview {
     WooShippingSplitShipmentsRow(viewModel: WooShippingSplitShipmentsViewModel(order: ShippingLabelSampleData.sampleOrder(),
-                                                                               config: ShippingLabelSampleData.sampleWooShippingConfig()))
+                                                                               config: ShippingLabelSampleData.sampleWooShippingConfig(),
+                                                                               items: [ShippingLabelPackageItem(productOrVariationID: 1,
+                                                                                                                name: "Shirt",
+                                                                                                                weight: 0.5,
+                                                                                                                quantity: 2,
+                                                                                                                value: 9.99,
+                                                                                                                dimensions: ProductDimensions(length: "",
+                                                                                                                                              width: "",
+                                                                                                                                              height: ""),
+                                                                                                                attributes: [],
+                                                                                                                imageURL: nil)]))
     .padding()
 }
 #endif
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
index 6fd43887c9b..8e0c5a12c52 100644
--- 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	
@@ -2,34 +2,125 @@ import SwiftUI
 import Yosemite
 import WooFoundation
 
+/// ViewModel for `WooShippingSplitShipmentsDetailView`
 final class WooShippingSplitShipmentsViewModel: ObservableObject {
     private let order: Order
     private let stores: StoresManager
     private let config: WooShippingConfig
+    private let items: [ShippingLabelPackageItem]
+    private let currencySettings: CurrencySettings
+    private let shippingSettingsService: ShippingSettingsService
+
+    /// Label with the total number of items to ship.
+    @Published private(set) var itemsCountLabel: String = ""
+
+    /// Label with the total price for all items in the shipment.
+    @Published private(set) var itemsPriceLabel = ""
+
+    /// Label with the total weight for all items in the shipment.
+    private var itemsWeightLabel = ""
+
+    /// Label with the details of the items to ship.
+    /// Includes total weight and total price for all items in the current shipment.
+    var itemsDetailLabel: String {
+        "\(itemsWeightLabel) • \(itemsPriceLabel)"
+    }
+
+    @Published var selectedItemIDs: [String: [String]] = [:]
+
+    let shipmentCardViewModels: [CollapsibleShipmentCardViewModel]
 
-    /// 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(order: Order,
          config: WooShippingConfig,
-         stores: StoresManager = ServiceLocator.stores) {
+         items: [ShippingLabelPackageItem],
+         stores: StoresManager = ServiceLocator.stores,
+         currencySettings: CurrencySettings = ServiceLocator.currencySettings,
+         shippingSettingsService: ShippingSettingsService = ServiceLocator.shippingSettingsService) {
         self.order = order
         self.config = config
+        self.items = items
         self.stores = stores
+        self.currencySettings = currencySettings
+        self.shippingSettingsService = shippingSettingsService
+
+        self.shipmentCardViewModels = {
+            var viewModels = [CollapsibleShipmentCardViewModel]()
+            for item in items {
+                // TODO: #15303 Set IDs based on web logic
+                let childShipmentIds: [String] = {
+                    guard item.quantity > 1 else {
+                        return []
+                    }
+
+                    var children: [String] = []
+                    for quantity in 0..<item.quantity.intValue {
+                        let childShipmentId = "\(item.productOrVariationID)-sub-\(quantity)"
+                        children.append(childShipmentId)
+                    }
+                    return children
+                }()
+
+                let viewModel = CollapsibleShipmentCardViewModel(parentShipmentId: "\(item.productOrVariationID)",
+                                                                 childShipmentIds: childShipmentIds,
+                                                                 item: item,
+                                                                 currency: order.currency)
+                viewModels.append(viewModel)
+            }
+            return viewModels
+        }()
+
+        configureSectionHeader()
+    }
+
+    func selectAll() {
+        shipmentCardViewModels.forEach {
+            $0.selectAll()
+        }
+    }
+}
+
+private extension WooShippingSplitShipmentsViewModel {
+    /// Configures the labels in the section header.
+    ///
+    func configureSectionHeader() {
+        let itemsCount = items.map(\.quantity).reduce(0, +)
+        itemsCountLabel = Localization.itemsCount(itemsCount)
+        itemsWeightLabel = formatWeight(for: items)
+        itemsPriceLabel = formatPrice(for: items)
+    }
+
+    /// Calculates and formats the total weight of the given items based on each item's weight and quantity.
+    ///
+    func formatWeight(for items: [ShippingLabelPackageItem]) -> String {
+        let totalWeight = items
+            .map { item in
+                item.weight * Double(truncating: item.quantity as NSDecimalNumber)
+            }
+            .reduce(0, +)
+        let weightFormatter = WeightFormatter(weightUnit: shippingSettingsService.weightUnit ?? "")
+        return weightFormatter.formatWeight(weight: totalWeight)
+    }
+
+    /// Calculates and formats the price of the given item based on the item quantity and unit price.
+    ///
+    func formatPrice(for items: [ShippingLabelPackageItem]) -> String {
+        let totalPrice = items.map { Decimal($0.value) * $0.quantity }.reduce(0, +)
+        let currencyFormatter = CurrencyFormatter(currencySettings: currencySettings)
+        return currencyFormatter.formatAmount(totalPrice, with: order.currency) ?? totalPrice.description
+    }
+}
+
+// MARK: Constants
+private extension WooShippingSplitShipmentsViewModel {
+    enum Localization {
+        static func itemsCount(_ count: Decimal) -> String {
+            return String.pluralize(count, singular: Localization.itemsCountSingularFormat, plural: Localization.itemsCountPluralFormat)
+        }
+        static let itemsCountSingularFormat = NSLocalizedString("wooShipping.createLabels.splitShipment.items.countSingular",
+                                                                value: "%1$@ item",
+                                                                comment: "Label for singular item to ship during shipping label creation. Reads like: '1 item'")
+        static let itemsCountPluralFormat = NSLocalizedString("wooShipping.createLabels.splitShipment.items.count",
+                                                              value: "%1$@ items",
+                                                              comment: "Label for plural items to ship during shipping label creation. Reads like: '3 items'")
     }
 }
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 8378e1d641b..62aa7479199 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	
@@ -446,6 +446,7 @@ private extension WooShippingCreateLabelsViewModel {
         if let config {
             splitShipmentsViewModel = WooShippingSplitShipmentsViewModel(order: order,
                                                                          config: config,
+                                                                         items: items.dataSource.items,
                                                                          stores: stores)
         }
     }
diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj
index caf14efd206..432e74091c5 100644
--- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj
+++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj
@@ -3032,6 +3032,11 @@
 		EEB4E2DE29B61AAD00371C3C /* CouponInputTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEB4E2DD29B61AAD00371C3C /* CouponInputTransformer.swift */; };
 		EEBA02A32ADD6005001FE8E4 /* BlazeCampaignDashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBA02A22ADD6005001FE8E4 /* BlazeCampaignDashboardView.swift */; };
 		EEBA02A52ADD606D001FE8E4 /* BlazeCampaignDashboardViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBA02A42ADD606D001FE8E4 /* BlazeCampaignDashboardViewModel.swift */; };
+		EEBB816F2D8C082B008D6CE5 /* CollapsibleShipmentCardViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBB816E2D8C0821008D6CE5 /* CollapsibleShipmentCardViewModel.swift */; };
+		EEBB81712D8C0839008D6CE5 /* CollapsibleShipmentCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBB81702D8C0834008D6CE5 /* CollapsibleShipmentCard.swift */; };
+		EEBB9B3B2D8E5071008D6CE5 /* SelectableShipmentRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBB9B3A2D8E5058008D6CE5 /* SelectableShipmentRow.swift */; };
+		EEBB9B3D2D8E5099008D6CE5 /* SelectableShipmentRowViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBB9B3C2D8E508E008D6CE5 /* SelectableShipmentRowViewModel.swift */; };
+		EEBB9B402D8FE5B6008D6CE5 /* WooShippingSplitShipmentsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBB9B3F2D8FE5B4008D6CE5 /* WooShippingSplitShipmentsViewModelTests.swift */; };
 		EEBDF7DA2A2EF69B00EFEF47 /* ShareProductCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBDF7D92A2EF69B00EFEF47 /* ShareProductCoordinator.swift */; };
 		EEBDF7DF2A2F674100EFEF47 /* ShareProductAIEligibilityChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBDF7DE2A2F674100EFEF47 /* ShareProductAIEligibilityChecker.swift */; };
 		EEBDF7E22A2F685C00EFEF47 /* DefaultShareProductAIEligibilityCheckerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBDF7E12A2F685C00EFEF47 /* DefaultShareProductAIEligibilityCheckerTests.swift */; };
@@ -6241,6 +6246,11 @@
 		EEB4E2DD29B61AAD00371C3C /* CouponInputTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CouponInputTransformer.swift; sourceTree = "<group>"; };
 		EEBA02A22ADD6005001FE8E4 /* BlazeCampaignDashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlazeCampaignDashboardView.swift; sourceTree = "<group>"; };
 		EEBA02A42ADD606D001FE8E4 /* BlazeCampaignDashboardViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlazeCampaignDashboardViewModel.swift; sourceTree = "<group>"; };
+		EEBB816E2D8C0821008D6CE5 /* CollapsibleShipmentCardViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleShipmentCardViewModel.swift; sourceTree = "<group>"; };
+		EEBB81702D8C0834008D6CE5 /* CollapsibleShipmentCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleShipmentCard.swift; sourceTree = "<group>"; };
+		EEBB9B3A2D8E5058008D6CE5 /* SelectableShipmentRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableShipmentRow.swift; sourceTree = "<group>"; };
+		EEBB9B3C2D8E508E008D6CE5 /* SelectableShipmentRowViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableShipmentRowViewModel.swift; sourceTree = "<group>"; };
+		EEBB9B3F2D8FE5B4008D6CE5 /* WooShippingSplitShipmentsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WooShippingSplitShipmentsViewModelTests.swift; sourceTree = "<group>"; };
 		EEBDF7D92A2EF69B00EFEF47 /* ShareProductCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareProductCoordinator.swift; sourceTree = "<group>"; };
 		EEBDF7DE2A2F674100EFEF47 /* ShareProductAIEligibilityChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareProductAIEligibilityChecker.swift; sourceTree = "<group>"; };
 		EEBDF7E12A2F685C00EFEF47 /* DefaultShareProductAIEligibilityCheckerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultShareProductAIEligibilityCheckerTests.swift; sourceTree = "<group>"; };
@@ -12502,6 +12512,7 @@
 		CEC3CC722C9343BC00B93FBE /* WooShipping Create Shipping Labels */ = {
 			isa = PBXGroup;
 			children = (
+				EEBB9B3E2D8FE5AC008D6CE5 /* Split shipments */,
 				B9A5317D2D2FCC3900208304 /* WooShipping Customs */,
 				CE2207032CA5C55100E16D9B /* WooShippingCreateLabelsViewModelTests.swift */,
 				CE7B4A572CA191F400F764EB /* WooShippingItemRowViewModelTests.swift */,
@@ -13996,6 +14007,10 @@
 		EE7E75A62D83EAD200E6FF5B /* WooShipping Split Shipments */ = {
 			isa = PBXGroup;
 			children = (
+				EEBB9B3C2D8E508E008D6CE5 /* SelectableShipmentRowViewModel.swift */,
+				EEBB9B3A2D8E5058008D6CE5 /* SelectableShipmentRow.swift */,
+				EEBB81702D8C0834008D6CE5 /* CollapsibleShipmentCard.swift */,
+				EEBB816E2D8C0821008D6CE5 /* CollapsibleShipmentCardViewModel.swift */,
 				EE7E75AB2D84080A00E6FF5B /* WooShippingSplitShipmentsViewModel.swift */,
 				EE7E75A92D84066800E6FF5B /* WooShippingSplitShipmentsDetailView.swift */,
 				EE7E75A72D83EB0700E6FF5B /* WooShippingSplitShipmentsRow.swift */,
@@ -14057,6 +14072,14 @@
 			path = Blaze;
 			sourceTree = "<group>";
 		};
+		EEBB9B3E2D8FE5AC008D6CE5 /* Split shipments */ = {
+			isa = PBXGroup;
+			children = (
+				EEBB9B3F2D8FE5B4008D6CE5 /* WooShippingSplitShipmentsViewModelTests.swift */,
+			);
+			path = "Split shipments";
+			sourceTree = "<group>";
+		};
 		EEBDF7D62A2EF65D00EFEF47 /* ShareProduct */ = {
 			isa = PBXGroup;
 			children = (
@@ -15862,6 +15885,7 @@
 				0211259F2578DE310075AD2A /* ShippingLabelPrintingStepView.swift in Sources */,
 				B58B4AB22108F01700076FDD /* NoticeView.swift in Sources */,
 				20D2CCA52C7E328300051705 /* POSModalCloseButton.swift in Sources */,
+				EEBB816F2D8C082B008D6CE5 /* CollapsibleShipmentCardViewModel.swift in Sources */,
 				DA25ADDD2C86145E00AE81FE /* MarkOrderAsReadUseCase.swift in Sources */,
 				01BB6C072D09DC560094D55B /* CardPresentModalLocationPreAlert.swift in Sources */,
 				74B5713621CD7604008F9B8E /* SharingHelper.swift in Sources */,
@@ -16524,6 +16548,7 @@
 				02DE39D92968647100BB31D4 /* DomainSettingsViewModel.swift in Sources */,
 				576EA39225264C7400AFC0B3 /* RefundConfirmationViewController.swift in Sources */,
 				DE36E0982A8634FF00B98496 /* StoreNameSetupView.swift in Sources */,
+				EEBB9B3B2D8E5071008D6CE5 /* SelectableShipmentRow.swift in Sources */,
 				2688641B25D3202B00821BA5 /* EditAttributesViewController.swift in Sources */,
 				7E6A01972725B811001668D5 /* FilterProductCategoryListViewController.swift in Sources */,
 				E10DFC7A2673595A0083AFF2 /* ShareSheet.swift in Sources */,
@@ -16751,6 +16776,7 @@
 				453DBF8E2387F34A006762A5 /* UICollectionViewCell+Helpers.swift in Sources */,
 				45B9C63E23A8E50D007FC4C5 /* ProductPriceSettingsViewController.swift in Sources */,
 				318853362639FC9C00F66A9C /* PaymentSettingsFlowPresentingViewController.swift in Sources */,
+				EEBB9B3D2D8E5099008D6CE5 /* SelectableShipmentRowViewModel.swift in Sources */,
 				452FE64B25657EC100EB54A0 /* LinkedProductsViewController.swift in Sources */,
 				B99686E32A13C98200D1AF62 /* ScanToPayViewModel.swift in Sources */,
 				450C6EEA286F4334002DB168 /* SitePlugin+Woo.swift in Sources */,
@@ -16765,6 +16791,7 @@
 				021125992578D9C20075AD2A /* ShippingLabelPrintingInstructionsView.swift in Sources */,
 				03E471D42942096B001A58AD /* BuiltInCardReaderPaymentAlertsProvider.swift in Sources */,
 				026826C72BF59E410036F959 /* PointOfSaleCardPresentPaymentScanningForReadersView.swift in Sources */,
+				EEBB81712D8C0839008D6CE5 /* CollapsibleShipmentCard.swift in Sources */,
 				68E952CC287536010095A23D /* SafariView.swift in Sources */,
 				D449C51C26DE6B5000D75B02 /* IconListItem.swift in Sources */,
 				EE9D03182B89E2B10077CED1 /* OrderStatusEnum+Analytics.swift in Sources */,
@@ -17891,6 +17918,7 @@
 				269B46642A16D6ED00ADA872 /* UpdateAnalyticsSettingsUseCaseTests.swift in Sources */,
 				AEFF77A829786A2900667F7A /* PriceInputViewControllerTests.swift in Sources */,
 				02C2756F24F5F5EE00286C04 /* ProductShippingSettingsViewModel+ProductVariationTests.swift in Sources */,
+				EEBB9B402D8FE5B6008D6CE5 /* WooShippingSplitShipmentsViewModelTests.swift in Sources */,
 				571FDDAE24C768DC00D486A5 /* MockZendeskManager.swift in Sources */,
 				45FBDF3C238D4EA800127F77 /* ExtendedAddProductImageCollectionViewCellTests.swift in Sources */,
 				2609797C2A13D31500442249 /* PrivacyBannerPresentationUseCaseTests.swift in Sources */,
diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/Split shipments/WooShippingSplitShipmentsViewModelTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/Split shipments/WooShippingSplitShipmentsViewModelTests.swift
new file mode 100644
index 00000000000..928f7198d9f
--- /dev/null
+++ b/WooCommerce/WooCommerceTests/ViewRelated/Shipping Label/WooShipping Create Shipping Labels/Split shipments/WooShippingSplitShipmentsViewModelTests.swift	
@@ -0,0 +1,106 @@
+import XCTest
+@testable import WooCommerce
+import WooFoundation
+import Yosemite
+
+final class WooShippingSplitShipmentsViewModelTests: XCTestCase {
+
+    private var currencySettings: CurrencySettings!
+    private var shippingSettingsService: MockShippingSettingsService!
+
+    let sampleOrder = Order.fake().copy(currency: "INR")
+
+    override func setUp() {
+        currencySettings = CurrencySettings()
+        shippingSettingsService = MockShippingSettingsService(dimensionUnit: "cm",
+                                                              weightUnit: "kg")
+    }
+
+    func test_inits_with_expected_values() throws {
+        // Given
+        let items = [sampleItem(id: 1, weight: 4, value: 10, quantity: 1),
+                     sampleItem(id: 2, weight: 3, value: 2.5, quantity: 1)]
+
+        // When
+        let viewModel = WooShippingSplitShipmentsViewModel(order: sampleOrder,
+                                                           config: WooShippingConfig.fake(),
+                                                           items: items,
+                                                           currencySettings: currencySettings,
+                                                           shippingSettingsService: shippingSettingsService)
+
+        // Then
+        assertEqual("2 items", viewModel.itemsCountLabel)
+        assertEqual("₹12.50", viewModel.itemsPriceLabel)
+        assertEqual("7 kg • ₹12.50", viewModel.itemsDetailLabel)
+    }
+
+    func test_total_items_count_label_handles_single_item() {
+        // Given
+        let items = [sampleItem(id: 1, weight: 1, value: 1, quantity: 1)]
+
+        // When
+        let viewModel = WooShippingSplitShipmentsViewModel(order: sampleOrder,
+                                                           config: WooShippingConfig.fake(),
+                                                           items: items,
+                                                           currencySettings: currencySettings,
+                                                           shippingSettingsService: shippingSettingsService)
+        // Then
+        assertEqual("1 item", viewModel.itemsCountLabel)
+    }
+
+    func test_total_items_count_label_handles_items_with_quantity_greater_than_one() {
+        // Given
+        let items = [sampleItem(id: 1, weight: 1, value: 1, quantity: 1),
+                     sampleItem(id: 2, weight: 1, value: 1, quantity: 2)]
+
+        // When
+        let viewModel = WooShippingSplitShipmentsViewModel(order: sampleOrder,
+                                                           config: WooShippingConfig.fake(),
+                                                           items: items,
+                                                           currencySettings: currencySettings,
+                                                           shippingSettingsService: shippingSettingsService)
+        // Then
+        assertEqual("3 items", viewModel.itemsCountLabel)
+    }
+
+    func test_total_items_detail_label_handles_items_with_quantity_greater_than_one() {
+        // Given
+        let items = [sampleItem(id: 1, weight: 5, value: 10, quantity: 2),
+                     sampleItem(id: 2, weight: 3, value: 2.5, quantity: 1)]
+
+        // When
+        let viewModel = WooShippingSplitShipmentsViewModel(order: sampleOrder,
+                                                           config: WooShippingConfig.fake(),
+                                                           items: items,
+                                                           currencySettings: currencySettings,
+                                                           shippingSettingsService: shippingSettingsService)
+
+        // Then
+        assertEqual("13 kg • ₹22.50", viewModel.itemsDetailLabel)
+    }
+
+}
+
+private extension WooShippingSplitShipmentsViewModelTests {
+    func sampleItem(id: Int64, weight: Double, value: Double, quantity: Decimal) -> ShippingLabelPackageItem {
+        ShippingLabelPackageItem(productOrVariationID: id,
+                                 name: "Item",
+                                 weight: weight,
+                                 quantity: quantity,
+                                 value: value,
+                                 dimensions: ProductDimensions(length: "20", width: "35", height: "5"),
+                                 attributes: [],
+                                 imageURL: nil)
+    }
+}
+
+private final class MockDataSource: WooShippingItemsDataSource {
+    var items: [ShippingLabelPackageItem]
+    var currency: String
+
+    init(items: [ShippingLabelPackageItem],
+         currency: String = "GBP") {
+        self.items = items
+        self.currency = currency
+    }
+}


Reviews:
user: login: itsmeichigo
body: I tested on simulator iPhone 16 Pro iOS 18.2 and confirmed that the UI looks good in general. I left some nit-picking in the comments, but I'm approving the PR to avoid blocking development.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T03:56:44Z
user: login: selanthiraiyan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T05:06:29Z
user: login: selanthiraiyan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T05:07:04Z
user: login: selanthiraiyan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T05:07:15Z
user: login: selanthiraiyan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T05:07:55Z
user: login: selanthiraiyan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T05:08:33Z
user: login: selanthiraiyan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T05:16:43Z
user: login: selanthiraiyan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T05:27:05Z
user: login: itsmeichigo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T05:59:56Z
user: login: itsmeichigo
body: Thanks for the updates! The changes look good to me 👍 

I left a comment in a previous thread about fixing the missing border for the badge, but it's ok to fix it later too.

I also found that Split shipment button is also available in an existing label, which I missed in a previous PR. Please feel free to fix it in a subsequent PR.

<img src="https://github.com/user-attachments/assets/16ba2c8d-f29e-47fa-a883-64a6b29474df" width=320 />

state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T06:02:19Z

Review comments:
diff_hunk: @@ -0,0 +1,72 @@
+import Yosemite
+import SwiftUI
+
+/// Displays a single collapsible shipment item row or grouped parent and child shipment item rows
+struct CollapsibleShipmentCard: View {
+    @State private var isCollapsed: Bool = true
+
+    private let viewModel: CollapsibleShipmentCardViewModel
+
+    init(viewModel: CollapsibleShipmentCardViewModel) {
+        self.viewModel = viewModel
+    }
+
+    var body: some View {
+        VStack(alignment: .leading, spacing: 0) {
+            mainShipmentRow
+                .padding(.horizontal, Layout.horizontalPadding)
+                .padding(.vertical, Layout.verticalPadding)
+                .background(Color(.listForeground(modal: false)))
+
+            if !isCollapsed {
+                VStack(spacing: 0) {
+                    ForEach(viewModel.childShipmentRows) { item in
+                        VStack(spacing: 0) {
+                            Divider()
+
+                            SelectableShipmentRow(viewModel: item)
+                                .padding(.leading, Layout.horizontalPadding * 2)
+                                .padding(.trailing, Layout.horizontalPadding)
+                                .padding(.vertical, Layout.verticalPadding)
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCard.swift
user: login: itsmeichigo
body: Should the child shipment rows have the same background color as the main shipment row? Currently there are two different row colors in dark mode: 

<img width="366" alt="image" src="https://github.com/user-attachments/assets/30471579-9d0a-42c6-8a94-8446b14bb487" />


created_at: 2025-03-24T03:15:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,72 @@
+import Yosemite
+import SwiftUI
+
+/// Displays a single collapsible shipment item row or grouped parent and child shipment item rows
+struct CollapsibleShipmentCard: View {
+    @State private var isCollapsed: Bool = true
+
+    private let viewModel: CollapsibleShipmentCardViewModel
+
+    init(viewModel: CollapsibleShipmentCardViewModel) {
+        self.viewModel = viewModel
+    }
+
+    var body: some View {
+        VStack(alignment: .leading, spacing: 0) {
+            mainShipmentRow
+                .padding(.horizontal, Layout.horizontalPadding)
+                .padding(.vertical, Layout.verticalPadding)
+                .background(Color(.listForeground(modal: false)))
+
+            if !isCollapsed {
+                VStack(spacing: 0) {
+                    ForEach(viewModel.childShipmentRows) { item in
+                        VStack(spacing: 0) {
+                            Divider()
+
+                            SelectableShipmentRow(viewModel: item)
+                                .padding(.leading, Layout.horizontalPadding * 2)
+                                .padding(.trailing, Layout.horizontalPadding)
+                                .padding(.vertical, Layout.verticalPadding)
+                        }
+                    }
+                }
+            }
+        }
+        .frame(maxWidth: .infinity, alignment: .center)
+        .roundedBorder(cornerRadius: Layout.borderCornerRadius, lineColor: Color(.separator), lineWidth: Layout.borderWidth)
+    }
+}
+
+private extension CollapsibleShipmentCard {
+    @ViewBuilder
+    var mainShipmentRow: some View {
+        if viewModel.childShipmentRows.isEmpty {
+            SelectableShipmentRow(viewModel: viewModel.mainShipmentRow)
+        } else {
+            Button(action: {
+                withAnimation {
+                    isCollapsed.toggle()
+                }
+            }, label: {
+                ZStack(alignment: .topTrailing) {
+                    SelectableShipmentRow(viewModel: viewModel.mainShipmentRow)
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCard.swift
user: login: itsmeichigo
body: Do we need to allow toggling when tapping on any white space in the row? We might need the `contentShape` modifier for that. Currently, the row is only toggled when tapping either on the chevron icon or on the image/labels.
created_at: 2025-03-24T03:18:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,60 @@
+import SwiftUI
+import WooFoundation
+import Yosemite
+
+/// View model for `CollapsibleShipmentCard`.
+final class CollapsibleShipmentCardViewModel: ObservableObject, Identifiable {
+    let id = UUID()
+
+    /// The main shipment row.
+    let mainShipmentRow: SelectableShipmentRowViewModel
+
+    /// Child shipment rows, if the shipment has more than one quantity
+    let childShipmentRows: [SelectableShipmentRowViewModel]
+
+    init(parentShipmentId: String,
+         childShipmentIds: [String],
+         item: ShippingLabelPackageItem,
+         currency: String) {
+        let mainShippingItem = WooShippingItemRowViewModel(item: ShippingLabelPackageItem(copy: item, quantity: max(1.0, Decimal(childShipmentIds.count))),
+                                                           currency: currency)
+        let childShippingItem = WooShippingItemRowViewModel(item: ShippingLabelPackageItem(copy: item, quantity: 1.0),
+                                                            currency: currency)
+
+        self.mainShipmentRow = SelectableShipmentRowViewModel(shipmentId: parentShipmentId,
+                                                              isSelectable: true,
+                                                              item: mainShippingItem,
+                                                              showQuantity: true)
+        self.childShipmentRows = childShipmentIds.map({
+            SelectableShipmentRowViewModel(shipmentId: $0,
+                                           isSelectable: true,
+                                           item: childShippingItem,
+                                           showQuantity: false)
+        })
+
+        observeSelection()
+    }
+
+    func selectAll() {
+        mainShipmentRow.setSelected(true)
+        childShipmentRows.forEach({ $0.setSelected(true) })
+    }
+}
+
+private extension CollapsibleShipmentCardViewModel {
+    func observeSelection() {
+        self.mainShipmentRow.onSelectedChange = { [weak self] row in
+            guard let self else { return }
+
+            self.childShipmentRows.forEach({ $0.setSelected(row.selected) })
+        }
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCardViewModel.swift
user: login: itsmeichigo
body: Super nit:

```suggestion
        mainShipmentRow.onSelectedChange = { [weak self] row in
            guard let self else { return }

            childShipmentRows.forEach({ $0.setSelected(row.selected) })
        }
```
created_at: 2025-03-24T03:22:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,60 @@
+import SwiftUI
+import WooFoundation
+import Yosemite
+
+/// View model for `CollapsibleShipmentCard`.
+final class CollapsibleShipmentCardViewModel: ObservableObject, Identifiable {
+    let id = UUID()
+
+    /// The main shipment row.
+    let mainShipmentRow: SelectableShipmentRowViewModel
+
+    /// Child shipment rows, if the shipment has more than one quantity
+    let childShipmentRows: [SelectableShipmentRowViewModel]
+
+    init(parentShipmentId: String,
+         childShipmentIds: [String],
+         item: ShippingLabelPackageItem,
+         currency: String) {
+        let mainShippingItem = WooShippingItemRowViewModel(item: ShippingLabelPackageItem(copy: item, quantity: max(1.0, Decimal(childShipmentIds.count))),
+                                                           currency: currency)
+        let childShippingItem = WooShippingItemRowViewModel(item: ShippingLabelPackageItem(copy: item, quantity: 1.0),
+                                                            currency: currency)
+
+        self.mainShipmentRow = SelectableShipmentRowViewModel(shipmentId: parentShipmentId,
+                                                              isSelectable: true,
+                                                              item: mainShippingItem,
+                                                              showQuantity: true)
+        self.childShipmentRows = childShipmentIds.map({
+            SelectableShipmentRowViewModel(shipmentId: $0,
+                                           isSelectable: true,
+                                           item: childShippingItem,
+                                           showQuantity: false)
+        })
+
+        observeSelection()
+    }
+
+    func selectAll() {
+        mainShipmentRow.setSelected(true)
+        childShipmentRows.forEach({ $0.setSelected(true) })
+    }
+}
+
+private extension CollapsibleShipmentCardViewModel {
+    func observeSelection() {
+        self.mainShipmentRow.onSelectedChange = { [weak self] row in
+            guard let self else { return }
+
+            self.childShipmentRows.forEach({ $0.setSelected(row.selected) })
+        }
+
+        self.childShipmentRows.forEach({
+            $0.onSelectedChange = { [weak self] row in
+                guard let self else { return }
+
+                self.mainShipmentRow.setSelected(false)
+            }
+        })
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCardViewModel.swift
user: login: itsmeichigo
body: Super nit:

```suggestion
        childShipmentRows.forEach({
            $0.onSelectedChange = { [weak self] row in
                guard let self else { return }

                mainShipmentRow.setSelected(false)
            }
        })
```
created_at: 2025-03-24T03:23:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+import Yosemite
+import SwiftUI
+
+/// Row for a selectable shipment item to ship with the Woo Shipping extension.
+struct SelectableShipmentRow: View {
+    @ObservedObject private var viewModel: SelectableShipmentRowViewModel
+
+    init(viewModel: SelectableShipmentRowViewModel) {
+        self.viewModel = viewModel
+    }
+
+    @ScaledMetric private var scale: CGFloat = 1
+
+    var body: some View {
+        AdaptiveStack(spacing: Layout.horizontalSpacing) {
+            if viewModel.isSelectable {
+                selectionCircle(selected: viewModel.selected)
+                    .contentShape(Rectangle())
+                    .onTapGesture {
+                        viewModel.handleTap()
+                    }
+            }
+
+            ProductImageThumbnail(productImageURL: viewModel.item.imageUrl,
+                                  productImageSize: Layout.imageSize,
+                                  scale: scale,
+                                  productImageCornerRadius: Layout.imageCornerRadius,
+                                  foregroundColor: Color(UIColor.listSmallIcon))
+            .overlay(alignment: .topTrailing) {
+                if viewModel.showQuantity {
+                    BadgeView(text: viewModel.item.quantityLabel,
+                              customizations: .init(textColor: .white, backgroundColor: .black),
+                              backgroundShape: badgeStyle)
+                    .offset(x: Layout.badgeOffset, y: -Layout.badgeOffset)
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/SelectableShipmentRow.swift
user: login: itsmeichigo
body: Nit: It looks to me that the badge is missing a white border around it when the label is longer.

<img width="115" alt="image" src="https://github.com/user-attachments/assets/bc6e4b3a-12b1-4ad2-bcda-3c2be1b815a9" />


created_at: 2025-03-24T03:26:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+import Yosemite
+import SwiftUI
+
+/// Row for a selectable shipment item to ship with the Woo Shipping extension.
+struct SelectableShipmentRow: View {
+    @ObservedObject private var viewModel: SelectableShipmentRowViewModel
+
+    init(viewModel: SelectableShipmentRowViewModel) {
+        self.viewModel = viewModel
+    }
+
+    @ScaledMetric private var scale: CGFloat = 1
+
+    var body: some View {
+        AdaptiveStack(spacing: Layout.horizontalSpacing) {
+            if viewModel.isSelectable {
+                selectionCircle(selected: viewModel.selected)
+                    .contentShape(Rectangle())
+                    .onTapGesture {
+                        viewModel.handleTap()
+                    }
+            }
+
+            ProductImageThumbnail(productImageURL: viewModel.item.imageUrl,
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/SelectableShipmentRow.swift
user: login: itsmeichigo
body: Nit: the thumbnails seem quite blurry compared to those on the order details screen. Please feel free to file a separate GH issue for this as the product selector screen seems to have the same issue.
created_at: 2025-03-24T03:38:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,72 @@
+import Yosemite
+import SwiftUI
+
+/// Displays a single collapsible shipment item row or grouped parent and child shipment item rows
+struct CollapsibleShipmentCard: View {
+    @State private var isCollapsed: Bool = true
+
+    private let viewModel: CollapsibleShipmentCardViewModel
+
+    init(viewModel: CollapsibleShipmentCardViewModel) {
+        self.viewModel = viewModel
+    }
+
+    var body: some View {
+        VStack(alignment: .leading, spacing: 0) {
+            mainShipmentRow
+                .padding(.horizontal, Layout.horizontalPadding)
+                .padding(.vertical, Layout.verticalPadding)
+                .background(Color(.listForeground(modal: false)))
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCard.swift
user: login: itsmeichigo
body: The view has a rounded border so the background view should be rounded as well to avoid showing sharp corners underneath (more visible in dark mode).

```suggestion
                .background(
                     RoundedRectangle(cornerRadius: Layout.borderCornerRadius)
                         .fill(Color(.listForeground(modal: false)))
                )
```
created_at: 2025-03-24T03:50:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,72 @@
+import Yosemite
+import SwiftUI
+
+/// Displays a single collapsible shipment item row or grouped parent and child shipment item rows
+struct CollapsibleShipmentCard: View {
+    @State private var isCollapsed: Bool = true
+
+    private let viewModel: CollapsibleShipmentCardViewModel
+
+    init(viewModel: CollapsibleShipmentCardViewModel) {
+        self.viewModel = viewModel
+    }
+
+    var body: some View {
+        VStack(alignment: .leading, spacing: 0) {
+            mainShipmentRow
+                .padding(.horizontal, Layout.horizontalPadding)
+                .padding(.vertical, Layout.verticalPadding)
+                .background(Color(.listForeground(modal: false)))
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCard.swift
user: login: selanthiraiyan
body: Good eye for design. 🕵️ 

I have updated the logic to apply corner radius based on current UI state in https://github.com/woocommerce/woocommerce-ios/pull/15397/commits/2c64d09fdd17e4ecb0d83076bc0e3ba69e2fa237 and https://github.com/woocommerce/woocommerce-ios/pull/15397/commits/36a64c6bb80321ac74081dd38579308bc6f29da6. 

| Before | After |
|--------|--------|
| ![Before](https://github.com/user-attachments/assets/1127f75f-6f04-4eea-b2ee-5ca07ab40d8e) | ![After](https://github.com/user-attachments/assets/b48c659e-fa01-4e60-a8f3-96c60cf37ac5) | 

The bleeding corners should be fixed now for both main and child shipment rows. 
created_at: 2025-03-24T05:06:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,60 @@
+import SwiftUI
+import WooFoundation
+import Yosemite
+
+/// View model for `CollapsibleShipmentCard`.
+final class CollapsibleShipmentCardViewModel: ObservableObject, Identifiable {
+    let id = UUID()
+
+    /// The main shipment row.
+    let mainShipmentRow: SelectableShipmentRowViewModel
+
+    /// Child shipment rows, if the shipment has more than one quantity
+    let childShipmentRows: [SelectableShipmentRowViewModel]
+
+    init(parentShipmentId: String,
+         childShipmentIds: [String],
+         item: ShippingLabelPackageItem,
+         currency: String) {
+        let mainShippingItem = WooShippingItemRowViewModel(item: ShippingLabelPackageItem(copy: item, quantity: max(1.0, Decimal(childShipmentIds.count))),
+                                                           currency: currency)
+        let childShippingItem = WooShippingItemRowViewModel(item: ShippingLabelPackageItem(copy: item, quantity: 1.0),
+                                                            currency: currency)
+
+        self.mainShipmentRow = SelectableShipmentRowViewModel(shipmentId: parentShipmentId,
+                                                              isSelectable: true,
+                                                              item: mainShippingItem,
+                                                              showQuantity: true)
+        self.childShipmentRows = childShipmentIds.map({
+            SelectableShipmentRowViewModel(shipmentId: $0,
+                                           isSelectable: true,
+                                           item: childShippingItem,
+                                           showQuantity: false)
+        })
+
+        observeSelection()
+    }
+
+    func selectAll() {
+        mainShipmentRow.setSelected(true)
+        childShipmentRows.forEach({ $0.setSelected(true) })
+    }
+}
+
+private extension CollapsibleShipmentCardViewModel {
+    func observeSelection() {
+        self.mainShipmentRow.onSelectedChange = { [weak self] row in
+            guard let self else { return }
+
+            self.childShipmentRows.forEach({ $0.setSelected(row.selected) })
+        }
+
+        self.childShipmentRows.forEach({
+            $0.onSelectedChange = { [weak self] row in
+                guard let self else { return }
+
+                self.mainShipmentRow.setSelected(false)
+            }
+        })
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCardViewModel.swift
user: login: selanthiraiyan
body: Done in https://github.com/woocommerce/woocommerce-ios/pull/15397/commits/0cdc5a3e86fd228a6576dc5559444115a206d1b6
created_at: 2025-03-24T05:07:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,60 @@
+import SwiftUI
+import WooFoundation
+import Yosemite
+
+/// View model for `CollapsibleShipmentCard`.
+final class CollapsibleShipmentCardViewModel: ObservableObject, Identifiable {
+    let id = UUID()
+
+    /// The main shipment row.
+    let mainShipmentRow: SelectableShipmentRowViewModel
+
+    /// Child shipment rows, if the shipment has more than one quantity
+    let childShipmentRows: [SelectableShipmentRowViewModel]
+
+    init(parentShipmentId: String,
+         childShipmentIds: [String],
+         item: ShippingLabelPackageItem,
+         currency: String) {
+        let mainShippingItem = WooShippingItemRowViewModel(item: ShippingLabelPackageItem(copy: item, quantity: max(1.0, Decimal(childShipmentIds.count))),
+                                                           currency: currency)
+        let childShippingItem = WooShippingItemRowViewModel(item: ShippingLabelPackageItem(copy: item, quantity: 1.0),
+                                                            currency: currency)
+
+        self.mainShipmentRow = SelectableShipmentRowViewModel(shipmentId: parentShipmentId,
+                                                              isSelectable: true,
+                                                              item: mainShippingItem,
+                                                              showQuantity: true)
+        self.childShipmentRows = childShipmentIds.map({
+            SelectableShipmentRowViewModel(shipmentId: $0,
+                                           isSelectable: true,
+                                           item: childShippingItem,
+                                           showQuantity: false)
+        })
+
+        observeSelection()
+    }
+
+    func selectAll() {
+        mainShipmentRow.setSelected(true)
+        childShipmentRows.forEach({ $0.setSelected(true) })
+    }
+}
+
+private extension CollapsibleShipmentCardViewModel {
+    func observeSelection() {
+        self.mainShipmentRow.onSelectedChange = { [weak self] row in
+            guard let self else { return }
+
+            self.childShipmentRows.forEach({ $0.setSelected(row.selected) })
+        }
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCardViewModel.swift
user: login: selanthiraiyan
body: Done in https://github.com/woocommerce/woocommerce-ios/pull/15397/commits/0cdc5a3e86fd228a6576dc5559444115a206d1b6
created_at: 2025-03-24T05:07:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,72 @@
+import Yosemite
+import SwiftUI
+
+/// Displays a single collapsible shipment item row or grouped parent and child shipment item rows
+struct CollapsibleShipmentCard: View {
+    @State private var isCollapsed: Bool = true
+
+    private let viewModel: CollapsibleShipmentCardViewModel
+
+    init(viewModel: CollapsibleShipmentCardViewModel) {
+        self.viewModel = viewModel
+    }
+
+    var body: some View {
+        VStack(alignment: .leading, spacing: 0) {
+            mainShipmentRow
+                .padding(.horizontal, Layout.horizontalPadding)
+                .padding(.vertical, Layout.verticalPadding)
+                .background(Color(.listForeground(modal: false)))
+
+            if !isCollapsed {
+                VStack(spacing: 0) {
+                    ForEach(viewModel.childShipmentRows) { item in
+                        VStack(spacing: 0) {
+                            Divider()
+
+                            SelectableShipmentRow(viewModel: item)
+                                .padding(.leading, Layout.horizontalPadding * 2)
+                                .padding(.trailing, Layout.horizontalPadding)
+                                .padding(.vertical, Layout.verticalPadding)
+                        }
+                    }
+                }
+            }
+        }
+        .frame(maxWidth: .infinity, alignment: .center)
+        .roundedBorder(cornerRadius: Layout.borderCornerRadius, lineColor: Color(.separator), lineWidth: Layout.borderWidth)
+    }
+}
+
+private extension CollapsibleShipmentCard {
+    @ViewBuilder
+    var mainShipmentRow: some View {
+        if viewModel.childShipmentRows.isEmpty {
+            SelectableShipmentRow(viewModel: viewModel.mainShipmentRow)
+        } else {
+            Button(action: {
+                withAnimation {
+                    isCollapsed.toggle()
+                }
+            }, label: {
+                ZStack(alignment: .topTrailing) {
+                    SelectableShipmentRow(viewModel: viewModel.mainShipmentRow)
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCard.swift
user: login: selanthiraiyan
body: Nice catch. Thanks for the suggestion. Applied it in https://github.com/woocommerce/woocommerce-ios/pull/15397/commits/f3a3f7e53bf7e4a30b79ba7f5f3d5ca7c8a8a8b4
created_at: 2025-03-24T05:07:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,72 @@
+import Yosemite
+import SwiftUI
+
+/// Displays a single collapsible shipment item row or grouped parent and child shipment item rows
+struct CollapsibleShipmentCard: View {
+    @State private var isCollapsed: Bool = true
+
+    private let viewModel: CollapsibleShipmentCardViewModel
+
+    init(viewModel: CollapsibleShipmentCardViewModel) {
+        self.viewModel = viewModel
+    }
+
+    var body: some View {
+        VStack(alignment: .leading, spacing: 0) {
+            mainShipmentRow
+                .padding(.horizontal, Layout.horizontalPadding)
+                .padding(.vertical, Layout.verticalPadding)
+                .background(Color(.listForeground(modal: false)))
+
+            if !isCollapsed {
+                VStack(spacing: 0) {
+                    ForEach(viewModel.childShipmentRows) { item in
+                        VStack(spacing: 0) {
+                            Divider()
+
+                            SelectableShipmentRow(viewModel: item)
+                                .padding(.leading, Layout.horizontalPadding * 2)
+                                .padding(.trailing, Layout.horizontalPadding)
+                                .padding(.vertical, Layout.verticalPadding)
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/CollapsibleShipmentCard.swift
user: login: selanthiraiyan
body: I missed it. Done in https://github.com/woocommerce/woocommerce-ios/pull/15397/commits/c0d7cbab4df85dfc226572e5a9161445aaad018b
created_at: 2025-03-24T05:08:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+import Yosemite
+import SwiftUI
+
+/// Row for a selectable shipment item to ship with the Woo Shipping extension.
+struct SelectableShipmentRow: View {
+    @ObservedObject private var viewModel: SelectableShipmentRowViewModel
+
+    init(viewModel: SelectableShipmentRowViewModel) {
+        self.viewModel = viewModel
+    }
+
+    @ScaledMetric private var scale: CGFloat = 1
+
+    var body: some View {
+        AdaptiveStack(spacing: Layout.horizontalSpacing) {
+            if viewModel.isSelectable {
+                selectionCircle(selected: viewModel.selected)
+                    .contentShape(Rectangle())
+                    .onTapGesture {
+                        viewModel.handleTap()
+                    }
+            }
+
+            ProductImageThumbnail(productImageURL: viewModel.item.imageUrl,
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/SelectableShipmentRow.swift
user: login: selanthiraiyan
body: 🕵️‍♀️ Logged an issue https://github.com/woocommerce/woocommerce-ios/issues/15401
created_at: 2025-03-24T05:16:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+import Yosemite
+import SwiftUI
+
+/// Row for a selectable shipment item to ship with the Woo Shipping extension.
+struct SelectableShipmentRow: View {
+    @ObservedObject private var viewModel: SelectableShipmentRowViewModel
+
+    init(viewModel: SelectableShipmentRowViewModel) {
+        self.viewModel = viewModel
+    }
+
+    @ScaledMetric private var scale: CGFloat = 1
+
+    var body: some View {
+        AdaptiveStack(spacing: Layout.horizontalSpacing) {
+            if viewModel.isSelectable {
+                selectionCircle(selected: viewModel.selected)
+                    .contentShape(Rectangle())
+                    .onTapGesture {
+                        viewModel.handleTap()
+                    }
+            }
+
+            ProductImageThumbnail(productImageURL: viewModel.item.imageUrl,
+                                  productImageSize: Layout.imageSize,
+                                  scale: scale,
+                                  productImageCornerRadius: Layout.imageCornerRadius,
+                                  foregroundColor: Color(UIColor.listSmallIcon))
+            .overlay(alignment: .topTrailing) {
+                if viewModel.showQuantity {
+                    BadgeView(text: viewModel.item.quantityLabel,
+                              customizations: .init(textColor: .white, backgroundColor: .black),
+                              backgroundShape: badgeStyle)
+                    .offset(x: Layout.badgeOffset, y: -Layout.badgeOffset)
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/SelectableShipmentRow.swift
user: login: selanthiraiyan
body: Good catch. I see that this applies to the order creation screen as well. I logged an issue to keep track. https://github.com/woocommerce/woocommerce-ios/issues/15402
created_at: 2025-03-24T05:27:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,96 @@
+import Yosemite
+import SwiftUI
+
+/// Row for a selectable shipment item to ship with the Woo Shipping extension.
+struct SelectableShipmentRow: View {
+    @ObservedObject private var viewModel: SelectableShipmentRowViewModel
+
+    init(viewModel: SelectableShipmentRowViewModel) {
+        self.viewModel = viewModel
+    }
+
+    @ScaledMetric private var scale: CGFloat = 1
+
+    var body: some View {
+        AdaptiveStack(spacing: Layout.horizontalSpacing) {
+            if viewModel.isSelectable {
+                selectionCircle(selected: viewModel.selected)
+                    .contentShape(Rectangle())
+                    .onTapGesture {
+                        viewModel.handleTap()
+                    }
+            }
+
+            ProductImageThumbnail(productImageURL: viewModel.item.imageUrl,
+                                  productImageSize: Layout.imageSize,
+                                  scale: scale,
+                                  productImageCornerRadius: Layout.imageCornerRadius,
+                                  foregroundColor: Color(UIColor.listSmallIcon))
+            .overlay(alignment: .topTrailing) {
+                if viewModel.showQuantity {
+                    BadgeView(text: viewModel.item.quantityLabel,
+                              customizations: .init(textColor: .white, backgroundColor: .black),
+                              backgroundShape: badgeStyle)
+                    .offset(x: Layout.badgeOffset, y: -Layout.badgeOffset)
path: WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/SelectableShipmentRow.swift
user: login: itsmeichigo
body: I think this can be quickly fixed by adding `stroke` to the background view of the `BadgeView` with style `roundedRectangle`. But fixing it in a separate PR sounds good to me too.
created_at: 2025-03-24T05:59:56Z
author_association: CONTRIBUTOR

PR comments:
user: login: dangermattic
created_at: 2025-03-23T10:50:45Z
body: <!--
  0 Errors
  1 Warning: This PR is larger than 300 lin...
  0 Messages
  0 Markdowns
-->
<table>
  <thead>
    <tr>
      <th width="50"></th>
      <th width="100%" data-danger-table="true" data-kind="Warning">
          1 Warning
      </th>
     </tr>
  </thead>
  <tbody>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">This PR is larger than 300 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.</td>
    </tr>
  </tbody>
</table>

<p align="right" data-meta="generated_by_pr-check">
  Generated by :no_entry_sign: <a href="https://danger.systems/">Danger</a>
</p>

author_association: COLLABORATOR
user: login: wpmobilebot
created_at: 2025-03-23T11:02:05Z
body: <!-- REUSE_ID: prototype-build-link -->

<p><img alt='WooCommerce iOS' align='top' src='https://appcenter-filemanagement-distrib1ede6f06e.azureedge.net/6116069e-a562-4b01-a190-a7239293c0bb/AppIcon60x60%402x.png?sv=2019-07-07&sr=c&sig=j1xTxh0%2BKbe21BPnKUXlwk%2F%2F3myWArHYoVUj%2FvBfkZw%3D&se=2025-03-31T05%3A14%3A25Z&sp=r' width='20px' />📲 You can test the changes from this Pull Request in <b>WooCommerce iOS</b> by scanning the QR code below to install the corresponding build.</p>
<table>
<tr>
  <td rowspan='6' width='260px'><img src='https://api.qrserver.com/v1/create-qr-code/?size=500x500&qzone=4&data=https%3A%2F%2Finstall.appcenter.ms%2Forgs%2Fautomattic%2Fapps%2FWooCommerce-Installable-Builds%2Freleases%2F13383' width='250' height='250' /></td>
  <td><b>App Name</b></td><td><img alt='WooCommerce iOS' align='top' src='https://appcenter-filemanagement-distrib1ede6f06e.azureedge.net/6116069e-a562-4b01-a190-a7239293c0bb/AppIcon60x60%402x.png?sv=2019-07-07&sr=c&sig=j1xTxh0%2BKbe21BPnKUXlwk%2F%2F3myWArHYoVUj%2FvBfkZw%3D&se=2025-03-31T05%3A14%3A25Z&sp=r' width='20px' /> WooCommerce iOS</td>
</tr>
<tr><td><b>Build Number</b></td><td>pr15397-2c64d09</td></tr>
<tr><td><b>Version</b></td><td>22.0</td></tr>
<tr><td><b>Bundle ID</b></td><td>com.automattic.alpha.woocommerce</td></tr>
<tr><td><b>Commit</b></td><td>2c64d09fdd17e4ecb0d83076bc0e3ba69e2fa237</td></tr>
<tr><td><b>App Center Build</b></td><td><a href='https://install.appcenter.ms/orgs/automattic/apps/WooCommerce-Installable-Builds/releases/13383'>WooCommerce - Prototype Builds #13383</a></td></tr>
</table>
<em>Automatticians: You can use our internal self-serve MC tool to give yourself access to App Center if needed.</em>

author_association: COLLABORATOR
user: login: selanthiraiyan
created_at: 2025-03-24T05:29:41Z
body: Thanks for the review and helpful suggestions, @itsmeichigo! 

- I have pushed some changes to address your comments. 
- I logged https://github.com/woocommerce/woocommerce-ios/issues/15401 and https://github.com/woocommerce/woocommerce-ios/issues/15402 to keep track of the issues that apply to the order creation screen as well. 

Please take another look at the PR when you can. Thanks! 🙇 
author_association: CONTRIBUTOR
user: login: selanthiraiyan
created_at: 2025-03-24T06:18:27Z
body: > I left a comment in a previous thread about fixing the missing border for the badge, but it's ok to fix it later too.

Thanks for the solution! Considering that this needs to be fixed on other screens with the badge as well, I will fix this in a separate PR.

> I also found that Split shipment button is also available in an existing label, which I missed in a previous PR. Please feel free to fix it in a subsequent PR.

Good catch. Logged a subtask in https://github.com/woocommerce/woocommerce-ios/issues/15305
author_association: CONTRIBUTOR