Skip to content

Commit 49573f4

Browse files
authored
Merge pull request #5374 from woocommerce/issue/5243-orders-date-range-filter
Order list: implemented the screens for showing the date range filters
2 parents ff70064 + d81d641 commit 49573f4

File tree

11 files changed

+622
-52
lines changed

11 files changed

+622
-52
lines changed

WooCommerce/Classes/ViewRelated/Filters/FilterListViewController.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ enum FilterListValueSelectorConfig {
5454
case staticOptions(options: [FilterType])
5555
// Filter list selector for categories linked to that site id, retrieved dynamically
5656
case productCategories(siteID: Int64)
57+
// Filter list selector for date range
58+
case ordersDateRange
5759
}
5860

5961
/// Contains data for rendering a filter type row.
@@ -234,6 +236,14 @@ private extension FilterListViewController {
234236
selectedCategory: selectedProductCategory,
235237
onProductCategorySelection: selectedValueAction)
236238
self.listSelector.navigationController?.pushViewController(filterProductCategoryListViewController, animated: true)
239+
case .ordersDateRange:
240+
let selectedOrderFilter = selected.selectedValue as? OrderDateRangeFilter
241+
let datesFilterVC = OrderDatesFilterViewController(selected: selectedOrderFilter) { dateRangeFilter in
242+
selected.selectedValue = dateRangeFilter
243+
self.updateUI(numberOfActiveFilters: self.viewModel.filterTypeViewModels.numberOfActiveFilters)
244+
self.listSelector.reloadData()
245+
}
246+
self.listSelector.navigationController?.pushViewController(datesFilterVC, animated: true)
237247
}
238248
}
239249
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
import UIKit
2+
3+
// MARK: - DateRangeFilterViewController
4+
// Allow to choose a range of dates (eg. used in Order Filters Date Range)
5+
//
6+
final class DateRangeFilterViewController: UIViewController {
7+
8+
@IBOutlet private weak var tableView: UITableView!
9+
10+
// Completion callback
11+
//
12+
typealias Completion = (_ startDate: Date?, _ endDate: Date?) -> Void
13+
private let onCompletion: Completion
14+
15+
private var startDate: Date?
16+
private var endDate: Date?
17+
18+
// Indicate when the start or the end date picker is expanded
19+
//
20+
private var startDateExpanded: Bool = false
21+
private var endDateExpanded: Bool = false
22+
23+
private var rows: [Row] = []
24+
25+
/// Init
26+
///
27+
init(startDate: Date?,
28+
endDate: Date?,
29+
completion: @escaping Completion) {
30+
self.startDate = startDate
31+
self.endDate = endDate
32+
onCompletion = completion
33+
super.init(nibName: nil, bundle: nil)
34+
}
35+
36+
required init?(coder: NSCoder) {
37+
fatalError("init(coder:) has not been implemented")
38+
}
39+
40+
override func viewDidLoad() {
41+
super.viewDidLoad()
42+
configureNavigationBar()
43+
configureMainView()
44+
updateRows()
45+
configureTableView()
46+
}
47+
}
48+
49+
// MARK: - View Configuration
50+
//
51+
private extension DateRangeFilterViewController {
52+
53+
func configureNavigationBar() {
54+
title = Localization.navigationBarTitle
55+
}
56+
57+
func configureMainView() {
58+
view.backgroundColor = .listBackground
59+
}
60+
61+
func configureTableView() {
62+
tableView.dataSource = self
63+
tableView.delegate = self
64+
65+
/// Registers all of the available TableViewCells
66+
///
67+
for row in Row.allCases {
68+
tableView.registerNib(for: row.type)
69+
}
70+
71+
tableView.backgroundColor = .listBackground
72+
tableView.removeLastCellSeparator()
73+
}
74+
75+
func updateRows() {
76+
var tempRows: [Row] = [.startDateTitle]
77+
if startDateExpanded {
78+
tempRows.append(.startDatePicker)
79+
}
80+
tempRows.append(.endDateTitle)
81+
if endDateExpanded {
82+
tempRows.append(.endDatePicker)
83+
}
84+
rows = tempRows
85+
}
86+
}
87+
88+
// MARK: - UITableViewDataSource Conformance
89+
//
90+
extension DateRangeFilterViewController: UITableViewDataSource {
91+
92+
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
93+
return rows.count
94+
}
95+
96+
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
97+
let row = rows[indexPath.row]
98+
let cell = tableView.dequeueReusableCell(withIdentifier: row.reuseIdentifier, for: indexPath)
99+
configure(cell, for: row, at: indexPath)
100+
return cell
101+
}
102+
}
103+
104+
// MARK: - UITableViewDelegate Conformance
105+
//
106+
extension DateRangeFilterViewController: UITableViewDelegate {
107+
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
108+
tableView.deselectRow(at: indexPath, animated: true)
109+
110+
switch rows[indexPath.row] {
111+
case .startDateTitle:
112+
startDateExpanded.toggle()
113+
updateRows()
114+
tableView.reloadData()
115+
case .endDateTitle:
116+
endDateExpanded.toggle()
117+
updateRows()
118+
tableView.reloadData()
119+
default:
120+
return
121+
}
122+
}
123+
124+
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
125+
return UITableView.automaticDimension
126+
}
127+
}
128+
129+
// MARK: - Cell configuration
130+
//
131+
private extension DateRangeFilterViewController {
132+
/// Cells currently configured in the order they appear on screen
133+
///
134+
func configure(_ cell: UITableViewCell, for row: Row, at indexPath: IndexPath) {
135+
switch cell {
136+
case let cell as TitleAndValueTableViewCell where row == .startDateTitle:
137+
configureStartDateTitle(cell: cell)
138+
break
139+
case let cell as DatePickerTableViewCell where row == .startDatePicker:
140+
configureStartDatePicker(cell: cell)
141+
break
142+
case let cell as TitleAndValueTableViewCell where row == .endDateTitle:
143+
configureEndDateTitle(cell: cell)
144+
break
145+
case let cell as DatePickerTableViewCell where row == .endDatePicker:
146+
configureEndDatePicker(cell: cell)
147+
break
148+
default:
149+
fatalError()
150+
}
151+
}
152+
153+
func configureStartDateTitle(cell: TitleAndValueTableViewCell) {
154+
cell.updateUI(title: Localization.startDateTitle, value: startDate?.toString(dateStyle: .medium, timeStyle: .none))
155+
cell.selectionStyle = .none
156+
}
157+
158+
func configureStartDatePicker(cell: DatePickerTableViewCell) {
159+
if let startDate = startDate {
160+
cell.getPicker().setDate(startDate, animated: false)
161+
}
162+
cell.getPicker().maximumDate = endDate
163+
cell.getPicker().preferredDatePickerStyle = .inline
164+
cell.onDateSelected = { [weak self] date in
165+
guard let self = self else {
166+
return
167+
}
168+
self.startDate = date
169+
self.tableView.reloadData()
170+
self.onCompletion(self.startDate, self.endDate)
171+
}
172+
cell.selectionStyle = .none
173+
}
174+
175+
func configureEndDateTitle(cell: TitleAndValueTableViewCell) {
176+
cell.updateUI(title: Localization.endDateTitle, value: endDate?.toString(dateStyle: .medium, timeStyle: .none))
177+
cell.selectionStyle = .none
178+
}
179+
180+
func configureEndDatePicker(cell: DatePickerTableViewCell) {
181+
if let endDate = endDate {
182+
cell.getPicker().setDate(endDate, animated: false)
183+
}
184+
cell.getPicker().minimumDate = startDate
185+
cell.getPicker().preferredDatePickerStyle = .inline
186+
cell.onDateSelected = { [weak self] date in
187+
guard let self = self else {
188+
return
189+
}
190+
self.endDate = date
191+
self.tableView.reloadData()
192+
self.onCompletion(self.startDate, self.endDate)
193+
}
194+
cell.selectionStyle = .none
195+
}
196+
}
197+
198+
// MARK: - Private Types
199+
//
200+
private extension DateRangeFilterViewController {
201+
202+
enum Row: CaseIterable {
203+
case startDateTitle
204+
case startDatePicker
205+
case endDateTitle
206+
case endDatePicker
207+
208+
fileprivate var type: UITableViewCell.Type {
209+
switch self {
210+
case .startDateTitle, .endDateTitle:
211+
return TitleAndValueTableViewCell.self
212+
case .startDatePicker, .endDatePicker:
213+
return DatePickerTableViewCell.self
214+
}
215+
}
216+
217+
fileprivate var reuseIdentifier: String {
218+
return type.reuseIdentifier
219+
}
220+
}
221+
}
222+
223+
private extension DateRangeFilterViewController {
224+
enum Localization {
225+
static let navigationBarTitle = NSLocalizedString("Custom Range",
226+
comment: "Navigation title of the orders filter selector screen for custom date range")
227+
static let startDateTitle = NSLocalizedString("Start Date", comment: "Label for one of the filters in order custom date range")
228+
static let endDateTitle = NSLocalizedString("End Date", comment: "Label for one of the filters in order custom date range")
229+
}
230+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
3+
<device id="retina6_1" orientation="portrait" appearance="light"/>
4+
<dependencies>
5+
<deployment identifier="iOS"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/>
7+
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
8+
<capability name="System colors in document resources" minToolsVersion="11.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" customClass="DateRangeFilterViewController" customModule="WooCommerce" customModuleProvider="target">
13+
<connections>
14+
<outlet property="tableView" destination="QoC-F3-gOr" id="nTf-lc-41c"/>
15+
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
16+
</connections>
17+
</placeholder>
18+
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
19+
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
20+
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
21+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
22+
<subviews>
23+
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="QoC-F3-gOr">
24+
<rect key="frame" x="0.0" y="44" width="414" height="818"/>
25+
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
26+
</tableView>
27+
</subviews>
28+
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
29+
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
30+
<constraints>
31+
<constraint firstItem="fnl-2z-Ty3" firstAttribute="bottom" secondItem="QoC-F3-gOr" secondAttribute="bottom" id="9AR-rl-kSK"/>
32+
<constraint firstItem="QoC-F3-gOr" firstAttribute="top" secondItem="fnl-2z-Ty3" secondAttribute="top" id="K1D-qx-85b"/>
33+
<constraint firstItem="QoC-F3-gOr" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" id="dmt-4n-vfU"/>
34+
<constraint firstItem="fnl-2z-Ty3" firstAttribute="trailing" secondItem="QoC-F3-gOr" secondAttribute="trailing" id="fIq-oU-4AX"/>
35+
</constraints>
36+
<point key="canvasLocation" x="131.8840579710145" y="91.741071428571431"/>
37+
</view>
38+
</objects>
39+
<resources>
40+
<systemColor name="systemBackgroundColor">
41+
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
42+
</systemColor>
43+
</resources>
44+
</document>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import Foundation
2+
import UIKit
3+
4+
/// Represents all of the possible Order Date Ranges in enum form + start and end date in case of custom dates
5+
///
6+
struct OrderDateRangeFilter: Equatable {
7+
var filter: OrderDateRangeFilterEnum
8+
var startDate: Date?
9+
var endDate: Date?
10+
}
11+
12+
// MARK: - FilterType conformance
13+
extension OrderDateRangeFilter: FilterType {
14+
/// Returns the localized text version of the Enum
15+
///
16+
var description: String {
17+
switch filter {
18+
case .any:
19+
return NSLocalizedString("Any", comment: "Label for one of the filters in order date range")
20+
case .today:
21+
return NSLocalizedString("Today", comment: "Label for one of the filters in order date range")
22+
case .last2Days:
23+
return NSLocalizedString("Last 2 Days", comment: "Label for one of the filters in order date range")
24+
case .last7Days:
25+
return NSLocalizedString("Last 7 Days", comment: "Label for one of the filters in order date range")
26+
case .last30Days:
27+
return NSLocalizedString("Last 30 Days", comment: "Label for one of the filters in order date range")
28+
case .custom:
29+
return NSLocalizedString("Custom Range", comment: "Label for one of the filters in order date range")
30+
}
31+
}
32+
33+
var isActive: Bool {
34+
return true
35+
}
36+
}
37+
38+
/// Represents all of the possible Order Date Ranges in enum form
39+
///
40+
enum OrderDateRangeFilterEnum: Hashable, CaseIterable {
41+
case any
42+
case today
43+
case last2Days
44+
case last7Days
45+
case last30Days
46+
case custom
47+
}
48+
49+
// MARK: - TableView utils
50+
extension OrderDateRangeFilterEnum {
51+
var cellType: UITableViewCell.Type {
52+
switch self {
53+
case .any, .today, .last2Days, .last7Days, .last30Days:
54+
return BasicTableViewCell.self
55+
case .custom:
56+
return TitleAndValueTableViewCell.self
57+
}
58+
}
59+
60+
var reuseIdentifier: String {
61+
return cellType.reuseIdentifier
62+
}
63+
}

0 commit comments

Comments
 (0)