Skip to content

Commit 6ad5f2d

Browse files
authored
Merge pull request #81 from woocommerce/issue/9-fullfill-order
Order Details: Fulfillment UI
2 parents 41a11fa + f24241a commit 6ad5f2d

File tree

10 files changed

+268
-57
lines changed

10 files changed

+268
-57
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import UIKit
2+
3+
extension UIButton {
4+
func applyFilledRoundStyle() {
5+
layer.borderColor = StyleManager.wooCommerceBrandColor.cgColor
6+
backgroundColor = StyleManager.wooCommerceBrandColor
7+
tintColor = .white
8+
layer.cornerRadius = 8.0
9+
}
10+
}

WooCommerce/Classes/ViewModels/OrderDetailsViewModel.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ class OrderDetailsViewModel {
3838
return summaryDate
3939
}
4040

41+
var items: [OrderItem] {
42+
return order.items
43+
}
44+
45+
let fulfillTitle = NSLocalizedString("Fulfill order", comment: "Fulfill order button title")
46+
4147
var paymentStatus: String {
4248
return order.status.description
4349
}

WooCommerce/Classes/ViewRelated/Orders/OrderDetails/OrderDetailsViewController.swift

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,40 +32,46 @@ class OrderDetailsViewController: UIViewController {
3232
}
3333

3434
func configureTableView() {
35+
tableView.estimatedSectionHeaderHeight = Constants.sectionHeight
36+
tableView.estimatedSectionFooterHeight = Constants.rowHeight
3537
tableView.estimatedRowHeight = Constants.rowHeight
3638
tableView.rowHeight = UITableViewAutomaticDimension
3739
configureSections()
3840
configureNibs()
3941
}
4042

4143
func configureSections() {
42-
let summarySection = Section(title: nil, footer: nil, rows: [.summary])
43-
let customerNoteSection = Section(title: NSLocalizedString("CUSTOMER PROVIDED NOTE", comment: "Customer note section title"), footer: nil, rows: [.customerNote])
44+
let summarySection = Section(leftTitle: nil, rightTitle: nil, footer: nil, rows: [.summary])
45+
46+
let productLeftTitle = NSLocalizedString("PRODUCT", comment: "Product section title")
47+
let productRightTitle = NSLocalizedString("QTY", comment: "Quantity abbreviation for section title")
48+
let productListSection = Section(leftTitle: productLeftTitle, rightTitle: productRightTitle, footer: nil, rows: [.productList])
49+
50+
let customerNoteSection = Section(leftTitle: NSLocalizedString("CUSTOMER PROVIDED NOTE", comment: "Customer note section title"), rightTitle: nil, footer: nil, rows: [.customerNote])
4451

4552
let infoFooter = billingIsHidden ? NSLocalizedString("Show billing", comment: "Footer text to show the billing cell") : NSLocalizedString("Hide billing", comment: "Footer text to hide the billing cell")
4653
let infoRows: [Row] = billingIsHidden ? [.shippingAddress] : [.shippingAddress, .billingAddress, .billingPhone, .billingEmail]
47-
let infoSection = Section(title: NSLocalizedString("CUSTOMER INFORMATION", comment: "Customer info section title"), footer: infoFooter, rows: infoRows)
54+
let infoSection = Section(leftTitle: NSLocalizedString("CUSTOMER INFORMATION", comment: "Customer info section title"), rightTitle: nil, footer: infoFooter, rows: infoRows)
4855

4956
var noteRows: [Row] = [.addOrderNote]
5057
if let notes = order.notes {
5158
for _ in notes {
5259
noteRows.append(.orderNote)
5360
}
5461
}
55-
let orderNotesSection = Section(title: NSLocalizedString("ORDER NOTES", comment: "Order notes section title"), footer: nil, rows: noteRows)
62+
let orderNotesSection = Section(leftTitle: NSLocalizedString("ORDER NOTES", comment: "Order notes section title"), rightTitle: nil, footer: nil, rows: noteRows)
5663

57-
let paymentSection = Section(title: NSLocalizedString("PAYMENT", comment: "Payment section title"), footer: nil, rows: [.payment])
64+
let paymentSection = Section(leftTitle: NSLocalizedString("PAYMENT", comment: "Payment section title"), rightTitle: nil, footer: nil, rows: [.payment])
5865

5966
// FIXME: this is temporary
6067
// the API response always sends customer note data
6168
// if there is no customer note it sends an empty string
6269
// but order has customerNote as an optional property right now
63-
guard let customerNote = order.customerNote,
64-
!customerNote.isEmpty else {
65-
sections = [summarySection, infoSection, paymentSection, orderNotesSection]
70+
guard let customerNote = order.customerNote, !customerNote.isEmpty else {
71+
sections = [summarySection, productListSection, infoSection, paymentSection, orderNotesSection]
6672
return
6773
}
68-
sections = [summarySection, customerNoteSection, infoSection, paymentSection, orderNotesSection]
74+
sections = [summarySection, productListSection, customerNoteSection, infoSection, paymentSection, orderNotesSection]
6975
}
7076

7177
func configureNibs() {
@@ -76,6 +82,8 @@ class OrderDetailsViewController: UIViewController {
7682
}
7783
}
7884

85+
let headerNib = UINib(nibName: TwoColumnSectionHeaderView.reuseIdentifier, bundle: nil)
86+
tableView.register(headerNib, forHeaderFooterViewReuseIdentifier: TwoColumnSectionHeaderView.reuseIdentifier)
7987
let footerNib = UINib(nibName: ShowHideSectionFooter.reuseIdentifier, bundle: nil)
8088
tableView.register(footerNib, forHeaderFooterViewReuseIdentifier: ShowHideSectionFooter.reuseIdentifier)
8189
}
@@ -100,16 +108,25 @@ extension OrderDetailsViewController: UITableViewDataSource {
100108
}
101109

102110
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
103-
guard let _ = sections[section].title else {
111+
if sections[section].leftTitle == nil {
104112
// iOS 11 table bug. Must return a tiny value to collapse `nil` or `empty` section headers.
105113
return CGFloat.leastNonzeroMagnitude
106114
}
107115

108116
return UITableViewAutomaticDimension
109117
}
110118

111-
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
112-
return sections[section].title
119+
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
120+
guard let leftText = sections[section].leftTitle else {
121+
return nil
122+
}
123+
124+
guard let cell = tableView.dequeueReusableHeaderFooterView(withIdentifier: TwoColumnSectionHeaderView.reuseIdentifier) as? TwoColumnSectionHeaderView else {
125+
fatalError()
126+
}
127+
cell.configure(leftText: leftText, rightText: sections[section].rightTitle)
128+
129+
return cell
113130
}
114131

115132
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
@@ -118,7 +135,7 @@ extension OrderDetailsViewController: UITableViewDataSource {
118135
return CGFloat.leastNonzeroMagnitude
119136
}
120137

121-
return Constants.rowHeight
138+
return UITableViewAutomaticDimension
122139
}
123140

124141
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
@@ -135,6 +152,7 @@ extension OrderDetailsViewController: UITableViewDataSource {
135152
let sections = IndexSet(integer: section)
136153
tableView.reloadSections(sections, with: .fade)
137154
}
155+
138156
return cell
139157
}
140158
}
@@ -158,6 +176,8 @@ extension OrderDetailsViewController {
158176
switch cell {
159177
case let cell as SummaryTableViewCell:
160178
cell.configure(with: viewModel)
179+
case let cell as ProductListTableViewCell:
180+
cell.configure(with: viewModel)
161181
case let cell as CustomerNoteTableViewCell:
162182
cell.configure(with: viewModel)
163183
case let cell as CustomerInfoTableViewCell where row == .shippingAddress:
@@ -283,16 +303,19 @@ extension OrderDetailsViewController: MFMailComposeViewControllerDelegate {
283303
private extension OrderDetailsViewController {
284304
struct Constants {
285305
static let rowHeight = CGFloat(38)
306+
static let sectionHeight = CGFloat(44)
286307
}
287308

288309
private struct Section {
289-
let title: String?
310+
let leftTitle: String?
311+
let rightTitle: String?
290312
let footer: String?
291313
let rows: [Row]
292314
}
293315

294316
private enum Row {
295317
case summary
318+
case productList
296319
case customerNote
297320
case shippingAddress
298321
case billingAddress
@@ -306,6 +329,8 @@ private extension OrderDetailsViewController {
306329
switch self {
307330
case .summary:
308331
return SummaryTableViewCell.reuseIdentifier
332+
case .productList:
333+
return ProductListTableViewCell.reuseIdentifier
309334
case .customerNote:
310335
return CustomerNoteTableViewCell.reuseIdentifier
311336
case .shippingAddress:
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import UIKit
2+
3+
class ProductListTableViewCell: UITableViewCell {
4+
@IBOutlet private var verticalStackView: UIStackView!
5+
@IBOutlet private var fulfillButton: UIButton!
6+
7+
static let reuseIdentifier = "ProductListTableViewCell"
8+
9+
override func setSelected(_ selected: Bool, animated: Bool) {
10+
super.setSelected(selected, animated: animated)
11+
backgroundColor = .white
12+
}
13+
}
14+
15+
extension ProductListTableViewCell {
16+
func configure(with viewModel: OrderDetailsViewModel) {
17+
for subView in verticalStackView.arrangedSubviews {
18+
subView.removeFromSuperview()
19+
}
20+
21+
for (index, item) in viewModel.items.enumerated() {
22+
let itemView = TwoColumnLabelView.makeFromNib()
23+
itemView.leftText = item.name
24+
itemView.rightText = item.quantity.description
25+
verticalStackView.insertArrangedSubview(itemView, at: index)
26+
}
27+
28+
fulfillButton.setTitle(viewModel.fulfillTitle, for: .normal)
29+
fulfillButton.applyFilledRoundStyle()
30+
31+
verticalStackView.setCustomSpacing(Constants.spacing, after: fulfillButton)
32+
}
33+
34+
struct Constants {
35+
static let spacing = CGFloat(8.0)
36+
}
37+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
3+
<device id="retina4_7" orientation="portrait">
4+
<adaptation id="fullscreen"/>
5+
</device>
6+
<dependencies>
7+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
8+
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
9+
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
10+
</dependencies>
11+
<objects>
12+
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
13+
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
14+
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="188" id="KGk-i7-Jjw" customClass="ProductListTableViewCell" customModule="WooCommerce" customModuleProvider="target">
15+
<rect key="frame" x="0.0" y="0.0" width="331" height="188"/>
16+
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
17+
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
18+
<rect key="frame" x="0.0" y="0.0" width="331" height="187.5"/>
19+
<autoresizingMask key="autoresizingMask"/>
20+
<subviews>
21+
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="UBp-2C-MWu">
22+
<rect key="frame" x="16" y="16" width="299" height="99.5"/>
23+
</stackView>
24+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Rng-8C-l5l">
25+
<rect key="frame" x="16" y="123.5" width="299" height="48"/>
26+
<constraints>
27+
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="48" id="egT-Dg-qmZ"/>
28+
</constraints>
29+
<state key="normal" title="Button"/>
30+
</button>
31+
</subviews>
32+
<constraints>
33+
<constraint firstItem="Rng-8C-l5l" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="1ki-E8-crI"/>
34+
<constraint firstItem="UBp-2C-MWu" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="4fY-7I-8Kh"/>
35+
<constraint firstItem="UBp-2C-MWu" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="16" id="5QQ-kw-gtJ"/>
36+
<constraint firstAttribute="trailing" secondItem="Rng-8C-l5l" secondAttribute="trailing" constant="16" id="CJh-nb-HvE"/>
37+
<constraint firstAttribute="trailing" secondItem="UBp-2C-MWu" secondAttribute="trailing" constant="16" id="WOB-jB-nRg"/>
38+
<constraint firstItem="Rng-8C-l5l" firstAttribute="top" secondItem="UBp-2C-MWu" secondAttribute="bottom" priority="750" constant="8" id="dbS-fW-98Y"/>
39+
<constraint firstAttribute="bottom" secondItem="Rng-8C-l5l" secondAttribute="bottom" constant="16" id="zHo-8e-b50"/>
40+
</constraints>
41+
</tableViewCellContentView>
42+
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
43+
<connections>
44+
<outlet property="fulfillButton" destination="Rng-8C-l5l" id="GrA-wO-Ocb"/>
45+
<outlet property="verticalStackView" destination="UBp-2C-MWu" id="AUM-Xi-GY1"/>
46+
</connections>
47+
<point key="canvasLocation" x="39.5" y="126"/>
48+
</tableViewCell>
49+
</objects>
50+
</document>

WooCommerce/Classes/ViewRelated/ReusableViews/TwoColumnLabelView.swift

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@ import UIKit
33
class TwoColumnLabelView: UIView {
44
@IBOutlet private var leftColumn: UILabel!
55
@IBOutlet private var rightColumn: UILabel!
6-
@IBOutlet private var topConstraint: NSLayoutConstraint!
7-
@IBOutlet private var bottomConstraint: NSLayoutConstraint!
86

97
enum Mode {
108
case body
119
case title
1210
}
1311

14-
var mode: Mode {
12+
var mode: Mode = .body {
1513
didSet {
1614
refreshStyle(mode: mode)
1715
}
@@ -43,24 +41,21 @@ class TwoColumnLabelView: UIView {
4341
case .title:
4442
leftColumn.applyTitleStyle()
4543
rightColumn.applyTitleStyle()
46-
topConstraint.constant = Constants.topConstant
47-
bottomConstraint.constant = Constants.bottomConstant
4844
}
4945
}
5046

5147
required init?(coder aDecoder: NSCoder) {
52-
mode = .body
48+
// initializers don't call property observers,
49+
// so don't set the default for mode here.
5350
super.init(coder: aDecoder)
5451
}
5552

56-
class func makeFromNib() -> TwoColumnLabelView {
57-
return Bundle.main.loadNibNamed("TwoColumnLabelView", owner: self, options: nil)?.first as! TwoColumnLabelView
53+
override func awakeFromNib() {
54+
super.awakeFromNib()
55+
mode = .body // trigger the property observer
5856
}
59-
}
6057

61-
extension TwoColumnLabelView {
62-
struct Constants {
63-
static let topConstant = CGFloat(14)
64-
static let bottomConstant = CGFloat(20)
58+
class func makeFromNib() -> TwoColumnLabelView {
59+
return Bundle.main.loadNibNamed("TwoColumnLabelView", owner: self, options: nil)?.first as! TwoColumnLabelView
6560
}
6661
}

0 commit comments

Comments
 (0)